From: <prn...@us...> - 2011-05-18 20:19:43
|
Revision: 8272 http://octave.svn.sourceforge.net/octave/?rev=8272&view=rev Author: prnienhuis Date: 2011-05-18 20:19:36 +0000 (Wed, 18 May 2011) Log Message: ----------- Experimental UNO support added, incl. newly created files Modified Paths: -------------- trunk/octave-forge/main/io/inst/oct2xls.m trunk/octave-forge/main/io/inst/xls2oct.m trunk/octave-forge/main/io/inst/xlsclose.m trunk/octave-forge/main/io/inst/xlsfinfo.m trunk/octave-forge/main/io/inst/xlsopen.m Modified: trunk/octave-forge/main/io/inst/oct2xls.m =================================================================== --- trunk/octave-forge/main/io/inst/oct2xls.m 2011-05-18 20:18:47 UTC (rev 8271) +++ trunk/octave-forge/main/io/inst/oct2xls.m 2011-05-18 20:19:36 UTC (rev 8272) @@ -1,4 +1,4 @@ -## Copyright (C) 2009,2010 Philip Nienhuis <prnienhuis at users.sf.net> +## Copyright (C) 2009,2010,2011 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 @@ -69,11 +69,12 @@ ## Beware that -if invoked- Excel invocations may be left running silently ## in case of COM errors. Invoke xlsclose with proper pointer struct to ## close them. -## When using java, note that large data array sizes elements may exhaust -## the java shared memory space for the default java memory settings. +## When using Java, note that large data array sizes elements may exhaust +## the Java shared memory space for the default java memory settings. ## For larger arrays, appropriate memory settings are needed in the file -## java.opts; then the maximum array size for the java-based spreadsheet -## options may be in the order of 10^6 elements. +## java.opts; then the maximum array size for the Java-based spreadsheet +## options may be in the order of 10^6 elements. In caso of UNO this +## limit is not applicable and spreadsheets may be much larger. ## ## Examples: ## @@ -103,8 +104,9 @@ ## 2010-12-01 Better check on file pointer struct (ischar (xls.xtype)) ## 2011-03-29 OpenXLS support added. Works but saving to file (xlsclose) doesn't work yet ## " Bug fixes (stray variable c_arr, and wrong test for valid xls struct) +## 2011-05-18 Experimental UNO support -## Last script file update (incl. subfunctions): 2011-03-29 (oct2oxs2xls) +## Last script file update (incl. subfunctions): 2011-05-18 (oct2uno2xls) function [ xls, rstatus ] = oct2xls (obj, xls, wsh=1, crange=[], spsh_opts=[]) @@ -160,9 +162,13 @@ elseif (strcmp (xls.xtype, 'JXL')) # Invoke Java and JExcelAPI [xls, rstatus] = oct2jxla2xls (obj, xls, wsh, crange, spsh_opts); -# elseif (strcmp (xls.xtype, 'OXS')) + elseif (strcmp (xls.xtype, 'OXS')) # # Invoke Java and OpenXLS ##### Not complete, saving file doesn't work yet! + printf ('Sorry, writing with OpenXLS not supported yet\n'); # [xls, rstatus] = oct2oxs2xls (obj, xls, wsh, crange, spsh_opts); + elseif (strcmp (xls.xtype, 'UNO')) + # Invoke Java and UNO bridge (OpenOffice.org) + [xls, rstatus] = oct2uno2xls (obj, xls, wsh, crange, spsh_opts); # elseif (strcmp'xls.xtype, '<whatever>')) # <Other Excel interfaces> else @@ -869,3 +875,137 @@ rstatus = 1; endfunction + + +## Copyright (C) 2011 Philip Nienhuis <prn...@us...> +## +## 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 2 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 Octave; see the file COPYING. If not, see +## <http://www.gnu.org/licenses/>. + +## oct2uno2xls + +## Author: Philip Nienhuis <prn...@us...> +## Created: 2011-05-18 + +function [ xls, rstatus ] = oct2uno2xls (c_arr, xls, wsh, crange, spsh_opts) + + changed = 0; + newsh = 0; + ctype = [1, 2, 3, 4, 5]; # Float, Logical, String, Formula, Empty + + # Get handle to sheet, create a new one if needed + sheets = xls.workbook.getSheets (); + sh_names = sheets.getElementNames (); + # Check sheet pointer + # FIXME sheet capacity check needed + if (isnumeric (wsh)) + if (wsh < 1) + error ("Illegal sheet index: %d", wsh); + elseif (wsh > numel (sh_names)) + # New sheet to be added. First create sheet name but check if it already exists + shname = sprintf ("Sheet%d", numel (sh_names) + 1); + jj = strmatch (wsh, {sh_names}); + if (~isempty (jj)) + # New sheet name already in file, try to create a unique & reasonable one + ii = 1; filler = ''; maxtry = 5; + while (ii <= maxtry) + shname = sprintf ("Sheet%s%d", [filler "_"], numel (sh_names + 1)); + if (isempty (strmatch (wsh, {sh_names}))) + ii = 10; + else + ++ii; + endif + endwhile + if (ii > maxtry + 1) + error ("Could not add sheet with a unique name to file %s"); + endif + endif + wsh = shname; + newsh = 1; + else + # turn wsh index into the associated sheet name + wsh = sh_names (wsh); + endif + else + # wsh is a sheet name. See if it exists already + if (isempty (strmatch (wsh, {sh_names}))) + # Not found. New sheet to be added + newsh = 1; + endif + endif + if (newsh) + # Add a new sheet. Sheet index MUST be a Java Short object + shptr = java_new ("java.lang.Short", sprintf ("%d", numel (sh_names) + 1)); + sh = sheets.insertNewByName (wsh, shptr); + sheets = xls.workbook.getSheets (); + sh_names = sheets.getElementNames (); + endif + # At this point we have a valid sheet name. Use it to get a sheet handle + unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.sheet.XSpreadsheet'); + sh = sheets.getByName (wsh).getObject.queryInterface (unotmp); + + # Check size of data array & range / capacity of worksheet & prepare vars + [nr, nc] = size (c_arr); + [topleft, nrows, ncols, trow, lcol] = spsh_chkrange (crange, nr, nc, xls.xtype, xls.filename); + --trow; --lcol; # Zero-based row # & col # + if (nrows < nr || ncols < nc) + warning ("Array truncated to fit in range"); + c_arr = c_arr(1:nrows, 1:ncols); + endif + + # Parse data array, setup typarr and throw out NaNs to speed up writing; + typearr = spsh_prstype (c_arr, nrows, ncols, ctype, spsh_opts, 0); + if ~(spsh_opts.formulas_as_text) + # Find formulas (designated by a string starting with "=" and ending in ")") + fptr = cellfun (@(x) ischar (x) && strncmp (x, "=", 1), c_arr); + typearr(fptr) = ctype(4); # FORMULA + endif + + # Transfer data to sheet + for ii=1:nrows + for jj=1:ncols + try + XCell = sh.getCellByPosition (lcol+jj-1, trow+ii-1); + switch typearr(ii, jj) + case 1 # Float + XCell.setValue (c_arr{ii, jj}); + case 2 # Logical. Convert to float + XCell.setValue (double (c_arr{ii, jj})); + case 3 # String + unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.text.XText'); + XCell.queryInterface (unotmp).setString (c_arr{ii, jj}); + case 4 # Formula + if (spsh_opts.formulas_as_text) + unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.text.XText'); + XCell.queryInterface (unotmp).setString (c_arr{ii, jj}); + else + XCell.setFormula (c_arr{ii, jj}); + endif + otherwise + # Empty cell + endswitch + changed = 1; + catch + printf ("Error writing cell %s (typearr() = %d)\n", calccelladdress(trow+ii, lcol+jj), typearr(ii, jj)); + keyboard + end_try_catch + endfor + endfor + + if (changed) + xls.changed = max (min (xls.changed, 2), changed); # Preserve 2 (new file), 1 (existing) + rstatus = 1; + endif + +endfunction Modified: trunk/octave-forge/main/io/inst/xls2oct.m =================================================================== --- trunk/octave-forge/main/io/inst/xls2oct.m 2011-05-18 20:18:47 UTC (rev 8271) +++ trunk/octave-forge/main/io/inst/xls2oct.m 2011-05-18 20:19:36 UTC (rev 8272) @@ -116,8 +116,9 @@ ## 2010-11-13 Catch empty sheets when no range was specified ## 2011-03-26 OpenXLS support added ## 2011-03-29 Test for proper input xls struct extended +## 2011-05-18 Experimental UNO support added ## -## Latest subfunc update: 2011-03-26 (OXS) +## Latest subfunc update: 2011-05-18 (UNO) function [ rawarr, xls, rstatus ] = xls2oct (xls, wsh=1, datrange='', spsh_opts=[]) @@ -163,6 +164,9 @@ elseif (strcmp (xls.xtype, 'OXS')) # Read xls file tru OpenXLS [rawarr, xls, rstatus] = xls2oxs2oct (xls, wsh, datrange, spsh_opts); + elseif (strcmp (xls.xtype, 'UNO')) + # Read xls file tru OpenOffice.org UNO (Java) bridge + [rawarr, xls, rstatus] = xls2uno2oct (xls, wsh, datrange, spsh_opts); # elseif ---- <Other interfaces here> # Call to next interface else @@ -843,3 +847,115 @@ xls.limits = [lcol, rcol; firstrow, lastrow]; endfunction + + +## Copyright (C) 2011 Philip Nienhuis <prn...@us...> +## +## 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 2 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 Octave; see the file COPYING. If not, see +## <http://www.gnu.org/licenses/>. + +## xls2uno2oct + +## Author: Philip Nienhuis <prn...@us...> +## Created: 2011-05-05 +## Updates: +## + +function [rawarr, xls, rstatus] = xls2uno2oct (xls, wsh, datrange, spsh_opts) + + sheets = xls.workbook.getSheets (); + sh_names = sheets.getElementNames (); + + # Check sheet pointer + if (isnumeric (wsh)) + if (wsh < 1 || wsh > numel (sh_names)) + error ("Sheet index %d out of range 1-%d", wsh, numel (sh_names)); + endif + else + ii = strmatch (wsh, {sh_names}); + if (isempty (ii)), error ("Sheet '%s' not found", wsh); endif + wsh = ii; + endif + unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.sheet.XSpreadsheet'); + sh = sheets.getByName(sh_names(wsh)).getObject.queryInterface (unotmp); + + unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.sheet.XCellRangesQuery'); + xRQ = sh.queryInterface (unotmp); + # Get cell ranges of all rectangles containing data. Type values: + #java_get ('com.sun.star.sheet.CellFlags', 'VALUE') ans = 1 + #java_get ('com.sun.star.sheet.CellFlags', 'DATETIME') ans = 2 + #java_get ('com.sun.star.sheet.CellFlags', 'STRING') ans = 4 + #java_get ('com.sun.star.sheet.CellFlags', 'FORMULA') ans = 16 + # Yep, boolean is lacking... + Cellflgs = javaObject ("java.lang.Short", "23"); + ccells = xRQ.queryContentCells (Cellflgs); + addrs = ccells.getRangeAddressesAsString (); + + # Strip sheet name from addresses + adrblks = strsplit (addrs, ','); + if (isempty (adrblks)) + warning ('Sheet %s contains no data', sh_names{wsh}); + return + endif + + # Either parse (given cell range) or prepare (unknown range) help variables. + # As OpenOffice knows the occupied range, we need the limits anyway to avoid + # out-of-range errors + [ trow, brow, lcol, rcol ] = getusedrange (xls, wsh); + if (isempty (datrange)) + nrows = brow - trow + 1; # Number of rows to be read + ncols = rcol - lcol + 1; # Number of columns to be read + else + [dummy, nrows, ncols, srow, scol] = parse_sp_range (datrange); + # Truncate range silently if needed + brow = min (srow + nrows - 1, brow); + rcol = min (scol + ncols - 1, rcol); + trow = max (trow, srow); + lcol = max (lcol, scol); + nrows = min (brow - trow + 1, nrows); # Number of rows to be read + ncols = min (rcol - lcol + 1, ncols); # Number of columns to be read + endif + # Create storage for data at Octave side + rawarr = cell (nrows, ncols); + + # Get data. Apparently row & column indices are 0-based in UNO + for ii=trow-1:brow-1 + for jj=lcol-1:rcol-1 + XCell = sh.getCellByPosition (jj, ii); + cType = XCell.getType().getValue (); + switch cType + case 1 # Value + rawarr{ii-trow+2, jj-lcol+2} = XCell.getValue (); + case 2 # String + unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.text.XText'); + rawarr{ii-trow+2, jj-lcol+2} = XCell.queryInterface (unotmp).getString (); + case 3 # Formula + if (spsh_opts.formulas_as_text) + rawarr{ii-trow+2, jj-lcol+2} = XCell.getFormula (); + else + unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.text.XText'); + rawarr{ii-trow+2, jj-lcol+2} = XCell.queryInterface (unotmp).getString (); + endif + otherwise + # Empty cell + endswitch + endfor + endfor + + # Keep track of data rectangle limits + xls.limits = [lcol, rcol; trow, brow]; + + rstatus = ~isempty (rawarr); + +endfunction Modified: trunk/octave-forge/main/io/inst/xlsclose.m =================================================================== --- trunk/octave-forge/main/io/inst/xlsclose.m 2011-05-18 20:18:47 UTC (rev 8271) +++ trunk/octave-forge/main/io/inst/xlsclose.m 2011-05-18 20:19:36 UTC (rev 8272) @@ -17,6 +17,7 @@ ## -*- texinfo -*- ## @deftypefn {Function File} [@var{xls}] = xlsclose (@var{xls}) ## @deftypefnx {Function File} [@var{xls}] = xlsclose (@var{xls}, @var{filename}) +## @deftypefnx {Function File} [@var{xls}] = xlsclose (@var{xls}, "FORCE") ## Close the Excel spreadsheet pointed to in struct @var{xls}, if needed ## write the file to disk. Based on information contained in @var{xls}, ## xlsclose will determine if the file should be written to disk. @@ -26,23 +27,25 @@ ## However if errors occurred, the file pinter will be ontouched so you can ## clean up before a next try with xlsclose(). ## Be warned that until xlsopen is called again with the same @var{xls} pointer -## struct and @var{_keepxls_} omitted or set to false, hidden Excel or Java -## applications with associated (possibly large) memory chunks are kept alive -## taking up resources. +## struct, hidden Excel or Java applications with associated (possibly large) +## memory chunks are kept in memory, taking up resources. +## If (string) argument "FORCE" is supplied, the file pointer will be reset +## regardless, whether the possibly modified file has been saved successfully +## or not. Hidden Excel (COM) or OpenOffice.org (UNO) invocations may live on, +## possibly even impeding proper shutdown of Octave. ## ## @var{filename} can be used to write changed spreadsheet files to ## an other file than opened with xlsopen(); unfortunately this doesn't work ## with JXL (JExcelAPI) interface. ## -## You need MS-Excel (95 - 2010), and/or the Java package > 1.2.6 plus Apache -## POI > 3.5 and/or JExcelAPI and/or OpenXLS installed on your computer + proper -## javaclasspath set, to make this function work at all. +## You need MS-Excel (95 - 2010), and/or the Java package => 1.2.8 plus Apache +## POI > 3.5 and/or JExcelAPI and/or OpenXLS and/or OpenOffice.org or clones +## installed on your computer + proper javaclasspath set, to make this +## function work at all. ## ## @var{xls} must be a valid pointer struct made by xlsopen() in the same ## octave session. ## -## Beware: Excel invocations may be left running invisibly in case of COM errors. -## ## Examples: ## ## @example @@ -66,31 +69,56 @@ ## 2010-11-12 Replaced 'keepxls' by new filename arg; catch write errors and ## always keep file pointer in case of write errors ## 2011-03-26 Added OpenXLS support +## 2011-05-18 Added experimental UNO support, incl. saving newly created files -function [ xls ] = xlsclose (xls, filename=[]) +function [ xls ] = xlsclose (xls, varargs) - if (~isempty (filename)) - if (ischar (filename)) - if (xls.changed == 0) - warning ("File %s wasn't changed, new filename ignored.", filename); - else - if (strcmp (xls.xtype, 'JXL')) - warning ("JXL doesn't support changing filename, new filename ignored."); - elseif ~(strcmp (xls.xtype, 'COM') || strmatch ('.xls', filename)) - # Excel / ActiveX will write any filename extension - error ('No .xls or .xlsx extension lacking in filename %s', filename); + force = 0; + + if (nargin > 1) + for ii=2:nargin + if (strcmp (lower (varargin{ii}), "force")) + # Close .ods anyway even if write errors occur + force = 1; + elseif (~isempty (strfind (tolower (varargin{ii}), '.'))) + # Apparently a file name + if (xls.changed == 0 || xls.changed > 2) + printf ("File %s wasn't changed, new filename ignored.", xls.filename); + elseif (strcmp (xls.xtype, 'JXL')) + error ("JXL doesn't support changing filename, new filename ignored."); + elseif ~((strcmp (xls.xtype, 'COM') || strcmp (xls.xtype, 'UNO')) && isempty (strfind ('filename', '.xls'))) + # Excel/ActiveX && OOo (UNO bridge) will write any valid filetype; POI/JXL/OXS need .xls[x] + error ('.xls or .xlsx extension lacking in filename %s', filename); else ### For multi-user environments, uncomment below AND relevant stanza in xlsopen # In case of COM, be sure to first close the open workbook - #if (strcmp (xls.xtype, \xC7OM')) + #if (strcmp (xls.xtype, 'COM')) # xls.app.Application.DisplayAlerts = 0; # xls.workbook.close(); # xls.app.Application.DisplayAlerts = 0; #endif + if (strcmp (xls.xtype, 'UNO')) + # If needed, turn filename into URL + if (~isempty (strmatch ("file:///", filename)) || ~isempty (strmatch ("http:///", filename))... + || ~isempty (strmatch ("ftp:///", filename)) || ~isempty (strmatch ("www:///", filename))) + # Seems in proper shape for OOo (at first sight) + else + # Transform into URL form + fname = canonicalize_file_name (strsplit (filename, filesep){end}); + # On Windows, change backslash file separator into forward slash + if (strcmp (filesep, "\\")) + tmp = strsplit (fname, filesep); + flen = numel (tmp); + tmp(2:2:2*flen) = tmp; + tmp(1:2:2*flen) = '/'; + filename = [ 'file://' tmp{:} ]; + endif + endif + endif xls.filename = filename; endif endif - endif + endfor endif if (strcmp (xls.xtype, 'COM')) @@ -185,12 +213,62 @@ xls.workbook.close (); endif + elseif (strcmp (xls.xtype, 'UNO')) + # Java & UNO bridge + try + if (xls.changed && xls.changed < 3) + # Workaround: + unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.frame.XModel'); + xModel = xls.workbook.queryInterface (unotmp); + unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.util.XModifiable'); + xModified = xModel.queryInterface (unotmp); + if (xModified.isModified ()) + unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.frame.XStorable'); # isReadonly() ? + xStore = xls.app.xComp.queryInterface (unotmp); + if (xls.changed == 2) + # Some trickery as Octave Java cannot create non-numeric arrays + lProps = javaArray ('com.sun.star.beans.PropertyValue', 1); + lProp = java_new ('com.sun.star.beans.PropertyValue', "Overwrite", 0, true, []); + lProps(1) = lProp; + # OK, store file + xStore.storeAsURL (xls.filename, lProps); + else + xStore.store (); + endif + endif + endif + xls.changed = -1; # Needed for check on properly shutting down OOo + # Workaround: + unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.frame.XModel'); + xModel = xls.app.xComp.queryInterface (unotmp); + unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.util.XCloseable'); + xClosbl = xModel.queryInterface (unotmp); + xClosbl.close (true); + unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.frame.XDesktop'); + xDesk = xls.app.aLoader.queryInterface (unotmp); + xDesk.terminate(); + xls.changed = 0; + catch + if (force) + # Force closing OOo + unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.frame.XDesktop'); + xDesk = xls.app.aLoader.queryInterface (unotmp); + xDesk.terminate(); + else + warning ("Error closing xls pointer (UNO)"); + endif + return + end_try_catch + # elseif <other interfaces here> endif - if (xls.changed) + if (xls.changed && xls.changed < 3) warning (sprintf ("File %s could not be saved. Read-only or in use elsewhere?\nFile pointer preserved.", xls.filename)); + if (force) + xls = []; + endif else xls = []; endif Modified: trunk/octave-forge/main/io/inst/xlsfinfo.m =================================================================== --- trunk/octave-forge/main/io/inst/xlsfinfo.m 2011-05-18 20:18:47 UTC (rev 8271) +++ trunk/octave-forge/main/io/inst/xlsfinfo.m 2011-05-18 20:19:36 UTC (rev 8272) @@ -81,6 +81,7 @@ ## 2010-10-10 Made output arg2 contain only address ranges (or other sheet type names) ## 2010-11-01 Added other file type strings for return arg #3 (fformat) ## 2011-03-26 Added OpenXLS support +## 2011-05-18 Experimental UNO support function [ filetype, sh_names, fformat ] = xlsfinfo (filename, reqintf=[]) @@ -88,7 +89,8 @@ persistent lstr2; lstr2 = length (str2); xls = xlsopen (filename, 0, reqintf); - + if (isempty (xls)); return; endif + toscreen = nargout < 1; xlWorksheet = -4167; xlChart = 4; @@ -182,7 +184,23 @@ sh_names(ii, 2) = "Empty or Chart"; endif endfor - if (sh_cnt > 0) fformat = "xlWorkbookNormal"; else, fformat = ''; endif + if (sh_cnt > 0); fformat = "xlWorkbookNormal"; else; fformat = ''; endif + + elseif (strcmp (xls.xtype, 'UNO')) + sheets = xls.workbook.getSheets (); + sheetnames = sheets.getElementNames (); # A Java object, NOT a cell array + sh_cnt = numel (sheetnames); + sh_names = cell (sh_cnt, 2); + for ii=1:sh_cnt + sh_names(ii, 1) = sheetnames(ii); + [ tr, lr, lc, rc ] = getusedrange (xls, ii); + if (tr) + sh_names(ii, 2) = sprintf ("%s:%s", calccelladdress (tr, lc), calccelladdress (lr, rc)); + else + sh_names(ii, 2) = "Empty or Chart"; + endif + endfor + if (sh_cnt > 0); fformat = "xlWorkbookNormal"; else; fformat = ''; endif # elseif <Other Excel interfaces below> @@ -190,6 +208,7 @@ error (sprintf ("xlsfinfo: unknown Excel .xls interface - %s.", xls.xtype)); endif + if (toscreen) # Echo sheet names to screen for ii=1:sh_cnt Modified: trunk/octave-forge/main/io/inst/xlsopen.m =================================================================== --- trunk/octave-forge/main/io/inst/xlsopen.m 2011-05-18 20:18:47 UTC (rev 8271) +++ trunk/octave-forge/main/io/inst/xlsopen.m 2011-05-18 20:19:36 UTC (rev 8272) @@ -25,19 +25,22 @@ ## Calling xlsopen without specifying a return argument is fairly useless! ## ## To make this function work at all, you need MS-Excel (95 - 2003), and/or -## the Java package > 1.2.6 plus either Apache POI > 3.5 or JExcelAPI or OpenXLS -## installed on your computer + proper javaclasspath set. These interfaces -## are referred to as COM, POI, JXL and OXS, resp., and are preferred in that -## order by default (depending on their presence). +## the Java package >= 1.2.8 plus Apache POI >= 3.5 and/or JExcelAPI and/or +## OpenXLS and/or OpenOffice.org (or clones) installed on your computer + +## proper javaclasspath set. These interfaces are referred to as COM, POI, +## JXL, OXS, and UNO, resp., and are preferred in that order by default +## (depending on their presence). ## For OOXML support, in addition to Apache POI support you also need the ## following jars in your javaclasspath: poi-ooxml-schemas-3.5.jar, -## xbean.jar and dom4j-1.6.1.jar (or later versions). +## xbean.jar and dom4j-1.6.1.jar (or later versions). Later OpenOffice.org +## versions (UNO) have support for OOXML as well. ## ## @var{filename} should be a valid .xls or xlsx Excel file name (including -## extension); but if you use the COM interface you can specify any extension -## that your installed Excel version can read AND write, using the Java -## interfaces only .xls or .xlsx are allowed. If @var{filename} does not -## contain any directory path, the file is saved in the current directory. +## extension). But if you use the COM interface you can specify any extension +## that your installed Excel version can read AND write; the same goes for UNO +## (OpenOffice.org). Using the other Java interfaces, only .xls or .xlsx are +## allowed. If @var{filename} does not contain any directory path, the file +## is saved in the current directory. ## ## If @var{readwrite} is set to 0 (default value) or omitted, the Excel file ## is opened for reading. If @var{readwrite} is set to True or 1, an Excel @@ -45,11 +48,13 @@ ## ## Optional input argument @var{reqintf} can be used to override the Excel ## interface that otherwise is automatically selected by xlsopen. Currently -## implemented interfaces (in order of preference) are 'COM' (Excel / COM), -## 'POI' (Java / Apache POI), 'JXL' (Java / JExcelAPI) or 'OXS' (OpenXLS). +## implemented interfaces (in order of preference) are 'COM' (Excel/COM), +## 'POI' (Java/Apache POI), 'JXL' (Java/JExcelAPI), 'OXS' (Java/OpenXLS), or +## 'UNO' (Java/OpenOffice.org). ## -## Beware: Excel invocations may be left running invisibly in case of COM errors -## or forgetting to close the file pointer. +## Beware: Excel invocations may be left running invisibly in case of COM +## errors or forgetting to close the file pointer. Similarly for OpenOffice.org +## which may even prevent Octave from being closed. ## ## Examples: ## @@ -68,8 +73,8 @@ ## Author: Philip Nienhuis ## Created: 2009-11-29 -## Updates: -## 2010-01-03 Added OOXML support +## Updates: +## 2010-01-03 Added OOXML support ## 2010-01-10 Changed (java) interface preference order to COM->POI->JXL ## 2010-01-16 Removed echoeing debug info in POI stanza ## 2010-03-01 Removed javaclasspath check for rt.jar @@ -90,8 +95,9 @@ ## 2010-12-01 Small bugfix - reset xlssupport in l. 102 ## 2010-12-06 Textual changes to info header ## 2011-03-26 OpenXLS support added +## 2011-05-18 Experimental UNO support added, incl. creating new spreadsheets ## -## 2011-03-26 Latest subfunction update +## 2011-05-18 Latest subfunction update function [ xls ] = xlsopen (filename, xwrite=0, reqinterface=[]) @@ -99,7 +105,7 @@ # xlsinterfaces.<intf> = [] (not yet checked), 0 (found to be unsupported) or 1 (OK) if (isempty (chkintf)); chkintf = 1; - xlsinterfaces = struct ('COM', [], 'POI', [], 'JXL', [], 'OXS', []); + xlsinterfaces = struct ('COM', [], 'POI', [], 'JXL', [], 'OXS', [], 'UNO', []); endif xlssupport = 0; @@ -114,7 +120,8 @@ if ~(ischar (reqinterface) || iscell (reqinterface)), usage ("Arg # 3 not recognized"); endif # Turn arg3 into cell array if needed if (~iscell (reqinterface)), reqinterface = {reqinterface}; endif - xlsinterfaces.COM = 0; xlsinterfaces.POI = 0; xlsinterfaces.JXL = 0; xlsinterfaces.OXS = 0; + xlsinterfaces.COM = 0; xlsinterfaces.POI = 0; xlsinterfaces.JXL = 0; + xlsinterfaces.OXS = 0; xlsinterfaces.UNO = 0; for ii=1:numel (reqinterface) reqintf = toupper (reqinterface {ii}); # Try to invoke requested interface(s) for this call. Check if it @@ -127,8 +134,10 @@ xlsinterfaces.JXL = []; elseif (strcmp (reqintf, 'OXS')) xlsinterfaces.OXS = []; + elseif (strcmp (reqintf, 'UNO')) + xlsinterfaces.UNO = []; else - usage (sprintf ("Unknown .xls interface \"%s\" requested. Only COM, POI, JXL or OXS supported\n", reqinterface{})); + usage (sprintf ("Unknown .xls interface \"%s\" requested. Only COM, POI, JXL, OXS or UNO supported\n", reqinterface{})); endif endfor printf ("Checking interface(s):\n"); @@ -139,7 +148,7 @@ for ii=1:numel (reqinterface) if (~xlsinterfaces.(toupper (reqinterface{ii}))) # No it aint - warning ("%s is not supported!", reqinterface{ii}); + warning ("%s is not supported!", toupper (reqinterface{ii})); else ++xlsintf_cnt; endif @@ -147,6 +156,7 @@ # Reset interface check indicator if no requested support found if (~xlsintf_cnt) chkintf = []; + xls = []; return endif endif @@ -189,7 +199,7 @@ # Keep track of which interface is selected xlssupport = 0; - # Interface preference order is defined below: currently COM -> POI -> JXL -> OXS + # Interface preference order is defined below: currently COM -> POI -> JXL -> OXS -> UNO if (xlsinterfaces.COM && ~xlssupport) # Excel functioning has been tested above & file exists, so we just invoke it app = actxserver ("Excel.Application"); @@ -292,6 +302,55 @@ end_try_catch endif + if (xlsinterfaces.UNO && ~xlssupport) + # First the file name must be transformed into a URL + if (~isempty (strmatch ("file:///", filename)) || ~isempty (strmatch ("http:///", filename))... + || ~isempty (strmatch ("ftp:///", filename)) || ~isempty (strmatch ("www:///", filename))) + # Seems in proper shape for OOo (at first sight) + else + # Transform into URL form + fname = canonicalize_file_name (strsplit (filename, filesep){end}); + # On Windows, change backslash file separator into forward slash + if (strcmp (filesep, "\\")) + tmp = strsplit (fname, filesep); + flen = numel (tmp); + tmp(2:2:2*flen) = tmp; + tmp(1:2:2*flen) = '/'; + filename = [ 'file://' tmp{:} ]; + endif + endif + try + xContext = java_invoke ("com.sun.star.comp.helper.Bootstrap", "bootstrap"); + xMCF = xContext.getServiceManager (); + oDesktop = xMCF.createInstanceWithContext ("com.sun.star.frame.Desktop", xContext); + # Workaround: + unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.frame.XComponentLoader'); + aLoader = oDesktop.queryInterface (unotmp); + # Some trickery as Octave Java cannot create non-numeric arrays + lProps = javaArray ('com.sun.star.beans.PropertyValue', 1); + lProp = java_new ('com.sun.star.beans.PropertyValue', "Hidden", 0, true, []); + lProps(1) = lProp; + if (xwrite > 2) + xComp = aLoader.loadComponentFromURL ("private:factory/scalc", "_blank", 0, lProps); + else + xComp = aLoader.loadComponentFromURL (filename, "_blank", 0, lProps); + endif + # Workaround: + unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.sheet.XSpreadsheetDocument'); + xSpdoc = xComp.queryInterface (unotmp); + # save in ods struct: + xls.xtype = 'UNO'; + xls.workbook = xSpdoc; # Needed to be able to close soffice in odsclose() + xls.filename = filename; + xls.app.xComp = xComp; # Needed to be able to close soffice in odsclose() + xls.app.aLoader = aLoader; # Needed to be able to close soffice in odsclose() + xls.odfvsn = 'UNO'; + xlssupport += 16; + catch + error ('Couldn''t open file %s using UNO', filename); + end_try_catch + endif + # if # ---- other interfaces # endif @@ -354,6 +413,7 @@ ## - Java & Apache POI ## - Java & JExcelAPI ## - Java & OpenXLS (only JRE >= 1.4 needed) +## - Java & UNO bridge (OpenOffice.org invocation) ## ## Examples: ## @@ -372,6 +432,7 @@ ## 2011-02-15 Adapted to javaclasspath calling style of java-1.2.8 pkg ## 2011-03-26 OpenXLS support added ## '' Bug fix: javaclasspath change wasn't picked up between calls with req.intf +## 2011-05-18 Experimental UNO support added function [xlsinterfaces] = getxlsinterfaces (xlsinterfaces) @@ -424,7 +485,9 @@ xlsinterfaces.POI = 0; xlsinterfaces.JXL = 0; xlsinterfaces.OXS = 0; - if ~(isempty (xlsinterfaces.POI) && isempty (xlsinterfaces.JXL) && isempty (xlsinterfaces.OXS)) + xlsinterfaces.UNO = 0; + if ~(isempty (xlsinterfaces.POI) && isempty (xlsinterfaces.JXL)... + && isempty (xlsinterfaces.OXS) && isempty (xlsinterfaces.UNO)) # Some Java-based interface requested but Java support is absent error ('No Java support found.'); else @@ -503,7 +566,34 @@ warning ("\n Not all classes (.jar) required for OXS in classpath"); endif endif - + + # Try Java & UNO + if (isempty (xlsinterfaces.UNO)) + printf ("Java/UNO bridge... "); + xlsinterfaces.UNO = 0; + # entries0(1) = not a jar but a directory (<00o_install_dir/program/>) + jpchk = 0; entries = {'program', 'unoil', 'jurt', 'juh', 'unoloader', 'ridl'}; + # Only under *nix we might use brute force: e.g., strfind (javaclasspath, classname) + # as javaclasspath is one long string. Under Windows however classpath is a cell array + # so we need the following more subtle, platform-independent approach: + for jj=1:numel (entries) + for ii=1:numel (jcp) + jcplst = strsplit (jcp{ii}, filesep); + jcpentry = jcplst {end}; + if (~isempty (strfind (lower (jcpentry), lower (entries{jj})))) + jpchk = jpchk + 1; + endif + endfor + endfor + if (jpchk >= numel (entries)) + xlsinterfaces.UNO = 1; + fprintf ('OK\n'); + chk1 = 1; + else + warning ('\nOne or more UNO classes (.jar) missing in javaclasspath'); + endif + endif + # ---- Other interfaces here, similar to the ones above endfunction \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |