From: <prn...@us...> - 2010-11-14 16:55:25
|
Revision: 7916 http://octave.svn.sourceforge.net/octave/?rev=7916&view=rev Author: prnienhuis Date: 2010-11-14 16:55:15 +0000 (Sun, 14 Nov 2010) Log Message: ----------- Various bug fixes; better tracking of changes to files; tested w jOpenDocument 1.2 final (which still has limitations / bugs) Modified Paths: -------------- trunk/octave-forge/main/io/inst/oct2ods.m trunk/octave-forge/main/io/inst/ods2oct.m trunk/octave-forge/main/io/inst/odsclose.m trunk/octave-forge/main/io/inst/odsopen.m trunk/octave-forge/main/io/inst/odsread.m trunk/octave-forge/main/io/inst/odswrite.m Modified: trunk/octave-forge/main/io/inst/oct2ods.m =================================================================== --- trunk/octave-forge/main/io/inst/oct2ods.m 2010-11-14 16:53:26 UTC (rev 7915) +++ trunk/octave-forge/main/io/inst/oct2ods.m 2010-11-14 16:55:15 UTC (rev 7916) @@ -1,4 +1,4 @@ -## Copyright (C) 2009 Philip Nienhuis <pr.nienhuis at users.sf.net> +## Copyright (C) 2009,2010 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 @@ -104,11 +104,16 @@ ## 2010-08-23 Added check on validity of ods file ptr ## " Experimental support for odfdom 0.8.6 (in separate subfunc, to be integrated later) ## 2010-08-25 Improved help text (java memory, ranges) +## 2010-10-27 Improved file change tracking tru ods.changed +## 2010-11-12 Better input argument checks +## 2010-11-13 Reset ods.limits when read was successful +## 2010-11-13 Added check for 2-D input array ## -## Last update of subfunctions below: 2010-08-23 +## Last update of subfunctions below: 2011-11-12 function [ ods, rstatus ] = oct2ods (c_arr, ods, wsh=1, crange=[], spsh_opts=[]) + if (nargin < 2) error ("oct2xls needs a minimum of 2 arguments."); endif # Check if input array is cell if (isempty (c_arr)) warning ("Request to write empty matrix - ignored."); @@ -122,21 +127,31 @@ elseif (~iscell (c_arr)) error ("oct2ods: input array neither cell nor numeric array"); endif - - # Check ods file ptr - odschk = 1; - odschk = odschk && isstruct(ods); - odschk = odschk && (~isempty (ods.filename)); - odschk = odschk && (ods.xtype == 'OTK' || ods.xtype == 'JOD'); - if (~odschk) error ("Arg # 2 is an invalid ods file ptr\n"); endif - - # Check and if needed initialize spsh_opts - if isempty (spsh_opts) + if (ndims (c_arr) > 2), error ("Only 2-dimensional arrays can be written to spreadsheet"); endif + # Check ods file pointer struct + test1 = ~isfield (ods, "xtype"); + test1 = test1 || ~isfield (ods, "workbook"); + test1 = test1 || isempty (ods.workbook); + test1 = test1 || isempty (ods.app); + if test1 + error ("Invalid ods file pointer struct"); + endif + # Check worksheet ptr + if (~(ischar (wsh) || isnumeric (wsh))), error ("Integer (index) or text (wsh name) expected for arg # 3"); endif + # Check range + if (~(isempty (crange) || ischar (crange))), error ("Character string (range) expected for arg # 4"); endif + # Various options + if (isempty (spsh_opts)) spsh_opts.formulas_as_text = 0; - # Other options here + # other options to be implemented here + elseif (isstruct (spsh_opts)) + if (~isfield (spsh_opts, 'formulas_as_text')), spsh_opts.formulas_as_text = 0; endif + # other options to be implemented here + else + error ("Structure expected for arg # 5"); endif - if (nargout < 1) printf ("Warning: no output spreadsheet file pointer specified as argument.\n"); endif + if (nargout < 1) printf ("Warning: no output spreadsheet file pointer specified.\n"); endif if (strcmp (ods.xtype, 'OTK')) # Write ods file tru Java & ODF toolkit. @@ -148,18 +163,17 @@ otherwise error ("Unsupported odfdom version"); endswitch - elseif (strcmp (ods.xtype, 'JOD')) # Write ods file tru Java & jOpenDocument. API still leaves lots to be wished... [ ods, rstatus ] = oct2jod2ods (c_arr, ods, wsh, crange); - -# elseif ---- < Other interfaces here > - +# elseif + # ---- < Other interfaces here > else error (sprintf ("ods2oct: unknown OpenOffice.org .ods interface - %s.", ods.xtype)); - endif + if (rstatus), ods.limits = []; endif + endfunction @@ -191,23 +205,25 @@ ## writing to an existing sheet. In that case one should beware of ## table-number-columns-repeated, table-number-rows-repeated, ## covered (merged) cells, incomplete tables and rows, etc. -## ODF toolkit does nothing to hide this from the user; you may -## sort it out all by yourself. +## ODF toolkit v. 0.7.5 does nothing to hide this from the user; +## you may sort it out all by yourself. ## Author: Philip Nienhuis <prn...@us...> ## Created: 2010-01-07 ## Updates: ## 2010-01-14 (finally seems to work OK) ## 2010-03-08 Some comment lines adapted -## 2010-03-25 Try-catch added f unpatched-for-booleans java-1.2.6 / 1.2.7 package +## 2010-03-25 Try-catch added f. unpatched-for-booleans java-1.2.6 / 1.2.7 package ## 2010-04-11 Changed all references to "cell" to "scell" to avoid reserved keyword -## Small bugfix for cases with empty left columns (wrong cell reference) +## " Small bugfix for cases with empty left columns (wrong cell reference) ## 2010-04-13 Fixed bug with stray cell copies beyond added data rectangle ## 2010-07-29 Added formula input support (based on xls patch by Benjamin Lindner) ## 2010-08-01 Added try-catch around formula input ## " Changed range arg to also allow just topleft cell ## 2010-08-03 Moved range checks and type array parsing to separate functions -## 2010-08-13 Fixed empty Sheet1 in case of new spreadsheets fix input text sheet name +## 2010-08-13 Fixed empty Sheet1 in case of new spreadsheets, fix input text sheet name +## 2010-10-27 Improved file change tracking tru ods.changed +## 2010-11-12 Improved file change tracking tru ods.changed function [ ods, rstatus ] = oct2jotk2ods (c_arr, ods, wsh, crange, spsh_opts) @@ -217,7 +233,7 @@ ctype = [1, 2, 3, 4, 5, 6, 7]; endif - rstatus = 0; changed = 0; f_errs = 0; + rstatus = 0; f_errs = 0; # Get some basic spreadsheet data from the pointer using ODFtoolkit odfcont = ods.workbook; @@ -248,7 +264,7 @@ endwhile if (ischar (wsh) && nr_of_sheets < 256) newsh = 1; endif else # Sheet index specified - if ((ods.changed == 2) || (wsh > nr_of_sheets && wsh < 256)) # Max nr of sheets = 256 + if ((ods.changed > 2) || (wsh > nr_of_sheets && wsh < 256)) # Max nr of sheets = 256 # Create a new sheet newsh = 1; elseif (wsh <=nr_of_sheets && wsh > 0) @@ -279,13 +295,12 @@ # Prepare worksheet for writing. If needed create new sheet if (newsh) - if (ods.changed == 2) + if (ods.changed > 2) # New spreadsheet. Prepare to use the default 1x1 first sheet. sh = sheets.item(0); else # Other sheets exist, create a new sheet. First the basics sh = java_new ('org.odftoolkit.odfdom.doc.table.OdfTable', odfcont); - changed = 1; # Append sheet to spreadsheet ( contentRoot) offsprdsh.appendChild (sh); # Rebuild sheets nodes @@ -303,7 +318,7 @@ wsh = sheets.getLength () - 1; endif # Fixup wsh pointer in case of new spreadsheet - if (ods.changed == 2) wsh = 0; endif + if (ods.changed > 2) wsh = 0; endif # Add table-column entry for style etc col = sh.addTableColumn (); @@ -349,7 +364,7 @@ # Only now add drow as otherwise for each cell an empty table-column is # inserted above the rows (odftoolkit bug?) sh.appendRow (drow); - if (ods.changed == 2) + if (ods.changed > 2) # In case of a completely new spreadsheet, delete the first initial 1-cell row # But check if it *is* a row... try @@ -363,6 +378,7 @@ nrow = drow.cloneNode (1); # Deep copy sh.appendRow (nrow); endfor + ods.changed = min (ods.changed, 2); # Keep 2 for new spshsht, 1 for existing + changed else # Existing sheet. We must be prepared for all situations, incomplete rows, @@ -393,7 +409,6 @@ # Apparently a nr-rows-repeated top table-row must be split, as the # first data row seems to be projected in it (1st while condition above!) row.removeAttribute ('table:number-rows-repeated'); - changed = 1; row.getCellAt (0).removeAttribute ('table:number-columns-repeated'); nrow = row.cloneNode (1); drow = nrow; # Future upper data array row @@ -421,7 +436,6 @@ sh.appendRow (row); drow.appendCell (dcell); sh.appendRow (drow); - changed = 1; endif endif @@ -438,13 +452,11 @@ scell.setTableNumberColumnsRepeatedAttribute (lcol + 1); row.appendCell (scell); sh.appendRow (row); - changed = 1; else # If needed expand nr-rows-repeated repcnt = row.getTableNumberRowsRepeatedAttribute (); if (repcnt > 1) row.removeAttribute ('table:number-rows-repeated'); - changed = 1; # Insert new table-rows above row until our new data space is complete. # Keep handle of upper new table-row as that's where data are added 1st drow = row.cloneNode (1); @@ -475,7 +487,6 @@ if (csplit > 0) # Apparently a nr-columns-repeated cell must be split scell.removeAttribute ('table:number-columns-repeated'); - changed = 1; ncell = scell.cloneNode (1); if (repcnt > 1) scell.setTableNumberColumnsRepeatedAttribute (repcnt - csplit); @@ -494,7 +505,6 @@ scell = dcell.cloneNode (1); dcell.setTableNumberColumnsRepeatedAttribute (-csplit); row.appendCell (dcell); - changed = 1; row.appendCell (scell); endif endif @@ -508,13 +518,11 @@ # Apparently end of row encountered. Add cell scell = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableCell', odfcont); scell = row.appendCell (scell); - changed = 1; else # If needed expand nr-cols-repeated repcnt = scell.getTableNumberColumnsRepeatedAttribute (); if (repcnt > 1) scell.removeAttribute ('table:number-columns-repeated'); - changed = 1; for kk=2:repcnt ncell = scell.cloneNode (1); row.insertBefore (ncell, scell.getNextSibling ()); @@ -525,7 +533,6 @@ while (scell.hasChildNodes ()) tmp = scell.getFirstChild (); scell.removeChild (tmp); - changed = 1; endwhile scell.removeAttribute ('table:formula'); endif @@ -594,7 +601,6 @@ otherwise # Nothing endswitch - changed = 1; scell = scell.getNextSibling (); @@ -607,10 +613,8 @@ if (f_errs) printf ("%d formula errors encountered - please check input array\n", f_errs); endif - if (changed) - ods.changed = 1; - rstatus = 1; - endif + ods.changed = max (min (ods.changed, 2), changed); # Preserve 2 (new file), 1 (existing) + rstatus = 1; endfunction @@ -651,6 +655,8 @@ ## 2010-06-05 odfdom 0.8.5 is there, next try.... ## 2010-06-## odfdom 0.8.5 dropped, too buggy ## 2010-08-22 odfdom 0.8.6 is there... seems to work with just one bug, easily worked around +## 2010-10-27 Improved file change tracking tru ods.changed +## 2010-11-12 Improved file change tracking tru ods.changed function [ ods, rstatus ] = oct3jotk2ods (c_arr, ods, wsh, crange, spsh_opts) @@ -718,8 +724,13 @@ # Prepare spreadsheet for writing (size, etc.). If needed create new sheet if (newsh) - # Create a new sheet using DOM API. This part works OK. - sh = sheets.get (nr_of_sheets - 1).newTable (spsh, nrows, ncols); + if (ods.changed > 2) + # New spreadsheet, use default first sheet + sh = sheets.get (0); + else + # Create a new sheet using DOM API. This part works OK. + sh = sheets.get (nr_of_sheets - 1).newTable (spsh, nrows, ncols); + endif changed = 1; if (isnumeric (wsh)) # Give sheet a name @@ -793,7 +804,7 @@ endfor if (changed) - ods.changed = 1; + ods.changed = max (min (ods.changed, 2), changed); # Preserve 2 (new file), 1 (existing) rstatus = 1; endif @@ -840,21 +851,11 @@ ## " Code cleanup ## 2010-08-13 Fixed bug of ignoring text sheet name in case of new spreadsheet ## 2010-08-15 Fixed bug with invalid first sheet in new spreadsheets +## 2010-10-27 Improved file change tracking tru ods.changed +## 2010-11-12 Improved file change tracking tru ods.changed function [ ods, rstatus ] = oct2jod2ods (c_arr, ods, wsh, crange) - # Check jOpenDocument version - sh = ods.workbook.getSheet (0); - cl = sh.getCellAt (0, 0); - try - # Versions 1.2b3+ have public getValueType () - cl.getValueType (); - ver = 3; - catch - # 1.2b2 has not - ver = 2; - end_try_catch - rstatus = 0; sh = []; changed = 0; # Get worksheet. Use first one if none given @@ -871,9 +872,9 @@ if (isempty (sh)) # Sheet number wsh didn't exist yet wsh = sprintf ("Sheet%d", wsh+1); - elseif (ods.changed == 2) + elseif (ods.changed > 2) sh.setName ('Sheet1'); - ods.changed = 0; + changed = 1; endif endif endif @@ -882,16 +883,17 @@ sh = ods.workbook.getSheet (wsh); if (isempty (sh)) # Still doesn't exist. Create sheet - if (ver == 3) - if (ods.changed == 2) + if (ods.odfvsn == 3) + if (ods.changed > 2) # 1st "new" -unnamed- sheet has already been made when creating the spreadsheet sh = ods.workbook.getSheet (0); sh.setName (wsh); - ods.changed = 0; + changed = 1; else # For existing spreadsheets printf ("Adding sheet '%s'\n", wsh); sh = ods.workbook.addSheet (sh_cnt, wsh); + changed = 1; endif else error ("jOpenDocument v. 1.2b2 does not support adding sheets - upgrade to v. 1.2b3\n"); @@ -950,6 +952,7 @@ val = c_arr {ii, jj}; if ((isnumeric (val) && ~isnan (val)) || ischar (val) || islogical (val)) try + sh.getCellAt (jj + lcol - 1, ii + trow - 1).clearValue(); jcell = sh.getCellAt (jj + lcol - 1, ii + trow - 1).setValue (val); changed = 1; catch @@ -961,7 +964,7 @@ endfor if (changed) - ods.changed = 1; + ods.changed = max (min (ods.changed, 2), changed); # Preserve 2 (new file), 1 (existing) rstatus = 1; endif Modified: trunk/octave-forge/main/io/inst/ods2oct.m =================================================================== --- trunk/octave-forge/main/io/inst/ods2oct.m 2010-11-14 16:53:26 UTC (rev 7915) +++ trunk/octave-forge/main/io/inst/ods2oct.m 2010-11-14 16:55:15 UTC (rev 7916) @@ -1,4 +1,4 @@ -## Copyright (C) 2009 Philip Nienhuis <pr.nienhuis at users.sf.net> +## Copyright (C) 2009,2010 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 @@ -110,26 +110,39 @@ ## 2010-08-25 Improved helptext (moved some text around) ## 2010-08-27 Added ods3jotk2oct - internal function for odfdom-0.8.6.jar ## " Extended check on spsh_opts (must be a struct) +## 2010-10-27 Moved cropping rawarr from empty outer rows & columns to here ## -## (Latest update of subfunctions below: 2010-08-27) +## (Latest update of subfunctions below: 2010-11-13) function [ rawarr, ods, rstatus ] = ods2oct (ods, wsh=1, datrange=[], spsh_opts=[]) - # Check ods file ptr - odschk = 1; - odschk = odschk && isstruct(ods); - odschk = odschk && (~isempty (ods.filename)); - odschk = odschk && (ods.xtype == 'OTK' || ods.xtype == 'JOD'); - if (~odschk) error ("Arg # 1 is an invalid ods file ptr\n"); endif - - if isempty (spsh_opts) + # Check if ods struct pointer seems valid + if (~isstruct (ods)), error ("File ptr struct expected for arg @ 1"); endif + test1 = ~isfield (ods, "xtype"); + test1 = test1 || ~isfield (ods, "workbook"); + test1 = test1 || isempty (ods.workbook); + test1 = test1 || isempty (ods.app); + if (test1) + error ("Arg #1 is an invalid ods file struct"); + endif + # Check worksheet ptr + if (~(ischar (wsh) || isnumeric (wsh))), error ("Integer (index) or text (wsh name) expected for arg # 2"); endif + # Check range + if (~(isempty (datrange) || ischar (datrange))), error ("Character string (range) expected for arg # 3"); endif + # Check & setup options struct + if (nargin < 4 || isempty (spsh_opts)) spsh_opts.formulas_as_text = 0; + spsh_opts.strip_array = 1; # Other options here - # elseif (~isstruct (spsh_opts)) error ("struct expected for OPTIONS argument (# 4)"); + else + if (~isfield (spsh_opts, 'formulas_as_text')), spsh_opts.formulas_as_text = 0; endif + if (~isfield (spsh_opts, 'strip_array')), spsh_opts.strip_array = 1; endif + % Future options: endif - + + # Select the proper interfaces if (strcmp (ods.xtype, 'OTK')) # Read ods file tru Java & ODF toolkit switch ods.odfvsn @@ -140,15 +153,36 @@ otherwise error ("Unsupported odfdom version or invalid ods file pointer."); endswitch - elseif (strcmp (ods.xtype, 'JOD')) + # Read ods file tru Java & jOpenDocument. JOD doesn't know about formulas :-( [rawarr, ods, rstatus] = ods2jod2oct (ods, wsh, datrange); - -# elseif ---- < Other interfaces here > - +# elseif + # ---- < Other interfaces here > else error (sprintf ("ods2oct: unknown OpenOffice.org .ods interface - %s.", ods.xtype)); + endif + # Optionally strip empty outer rows and columns & keep track of original data location + if (spsh_opts.strip_array) + emptr = cellfun ('isempty', rawarr); + if (all (all (emptr))) + rawarr = {}; + ods.limits= []; + else + nrows = size (rawarr, 1); ncols = size (rawarr, 2); + 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 + + # Crop outer rows and columns and update limits + rawarr = rawarr(irowt:irowb, icoll:icolr); + ods.limits = ods.limits + [icoll-1, icolr-ncols; irowt-1, irowb-nrows]; + endif endif endfunction @@ -156,7 +190,7 @@ #===================================================================== -## Copyright (C) 2009 Philip Nienhuis <prnienhuis _at- users.sf.net> +## Copyright (C) 2009,2010 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 @@ -186,6 +220,7 @@ ## "" Added call to getusedrange() for cases when no range was specified ## 2010-03-19 More code cleanup & fixes for bugs introduced 18/3/2010 8-() ## 2010-08-03 Added preliminary support for reading back formulas as text strings +## 2010-10-27 Moved cropping rawarr from empty outer rows & columns to caller function [ rawarr, ods, rstatus ] = ods2jotk2oct (ods, wsh, crange, spsh_opts) @@ -363,26 +398,8 @@ ++ii; endwhile - # Crop rawarr from all empty outer rows & columns - # & keep track of limits - emptr = cellfun ('isempty', rawarr); - if (all (all (emptr))) - rawarr = {}; - ods.limits= []; - else - 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 - # Crop textarray - rawarr = rawarr(irowt:irowb, icoll:icolr); - rstatus = 1; - ods.limits = [lcol+icoll-1, lcol+icolr-1; trow+irowt-1, trow+irowb-1]; - endif + # Keep track of data rectangle limits + ods.limits = [lcol, rcol; trow, brow]; endfunction @@ -410,7 +427,8 @@ ## Author: Philip Nienhuis <Philip@DESKPRN> ## Created: 2010-08-24. First workable version Aug 27, 2010 ## Updates: -## +## 2010-10-27 Moved cropping rawarr from empty outer rows & columns to caller +## 2010-11-13 Added workaround for reading text cells in files made by jOpenDocument 1.2bx function [ rawarr, ods, rstatus ] = ods3jotk2oct (ods, wsh, crange, spsh_opts) @@ -467,6 +485,7 @@ rcol = min (lcol + ncols - 1, 1024); ncols = min (ncols, 1024 - lcol + 1); nrows = min (nrows, 65536 - trow + 1); + brow = trow + nrows - 1; endif # Create storage for data content @@ -478,13 +497,19 @@ for jj=lcol:ncols+lcol-1; ocell = row.getCellByIndex (jj-1); if ~isempty (ocell) - otype = ocell.getValueType (); + otype = deblank (tolower (ocell.getValueType ())); if (spsh_opts.formulas_as_text) if ~isempty (ocell.getFormula ()) otype = 'formula'; endif endif - switch deblank (tolower (otype)) + # Provisions for catching jOpenDocument 1.2b bug where text cells + # haven't been assigned an <office:value-type='string'> attribute + if (~isempty (ocell)) + if (findstr ('<text:', char (ocell.getOdfElement ()))), otype = 'string'; endif + endif + # At last, read the data + switch otype case {'float', 'currency', 'percentage'} rawarr(ii-trow+1, jj-lcol+1) = ocell.getDoubleValue (); case 'date' @@ -534,39 +559,21 @@ case 'formula' rawarr(ii-trow+1, jj-lcol+1) = ocell.getFormula (); otherwise - # Nothing + # Nothing. endswitch endif endfor endfor - # Crop rawarr from all empty outer rows & columns just like Excel does - # & keep track of limits - emptr = cellfun ('isempty', rawarr); - if (all (all (emptr))) - rawarr = {}; - ods.limits= []; - else - 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 - # Crop textarray - rawarr = rawarr(irowt:irowb, icoll:icolr); - rstatus = 1; - ods.limits = [lcol+icoll-1, lcol+icolr-1; trow+irowt-1, trow+irowb-1]; - endif + # Keep track of data rectangle limits + ods.limits = [lcol, rcol; trow, brow]; endfunction #=========================================================================== -## Copyright (C) 2009 Philip Nienhuis <pr.nienhuis at users.sf.net> +## Copyright (C) 2009,2010 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 @@ -591,6 +598,8 @@ ## Last updates: ## 2010-08-12 Added separate stanzas for jOpenDocument v 1.2b3 and up. This version ## allows better cell type parsing and is therefore more reliable +## 2010-10-27 Moved cropping rawarr from empty outer rows & columns to here +## 2010-11-13 Added workaround for reading text cells in files made by jOpenDocument 1.2bx function [ rawarr, ods, rstatus] = ods2jod2oct (ods, wsh, crange) @@ -600,10 +609,8 @@ # Check jOpenDocument version sh = ods.workbook.getSheet (0); cl = sh.getCellAt (0, 0); - try + if (ods.odfvsn == 3) # 1.2b3+ has public getValueType () - cl.getValueType (); - ver = 3; persistent ctype; if (isempty (ctype)) BOOLEAN = char (java_get ('org.jopendocument.dom.ODValueType', 'BOOLEAN')); @@ -614,10 +621,10 @@ STRING = char (java_get ('org.jopendocument.dom.ODValueType', 'STRING')); TIME = char (java_get ('org.jopendocument.dom.ODValueType', 'TIME')); endif - catch - # 1.2b2 has not - ver = 2; - end_try_catch +# else +# # 1.2b2 has not +# ver = 2; + endif if (isnumeric (wsh)) wsh = wsh - 1; endif # Sheet INDEX starts at 0 # Check if sheet exists. If wsh = numeric, nonexistent sheets throw errors. @@ -630,13 +637,14 @@ if (isempty (sh)) error ("No sheet called '%s' present in file %s\n", wsh, ods.filename); endif - + # Either parse (given cell range) or prepare (unknown range) help variables if (isempty (crange)) - if (ver < 3) + if (ods.odfvsn < 3) error ("No empty read range allowed in jOpenDocument version 1.2b2") else - [ trow, brow, lcol, rcol ] = getusedrange (ods, wsh+1); + if (isnumeric (wsh)) wsh = wsh + 1; endif + [ trow, brow, lcol, rcol ] = getusedrange (ods, wsh); nrows = brow - trow + 1; # Number of rows to be read ncols = rcol - lcol + 1; # Number of columns to be read endif @@ -650,11 +658,54 @@ rcol = min (lcol + ncols - 1, 1024); ncols = min (ncols, 1024 - lcol + 1); nrows = min (nrows, 65536 - trow + 1); + brow= trow + nrows - 1; endif # Create storage for data content rawarr = cell (nrows, ncols); - if (ver == 2) + if (ods.odfvsn >= 3) + # Version 1.2b3+ + for ii=1:nrows + for jj = 1:ncols + try + scell = sh.getCellAt (lcol+jj-2, trow+ii-2); + sctype = char (scell.getValueType ()); + # Workaround for sheets written by jOpenDocument 1.2bx (no value-type attrb): + if (~isempty (scell)) + if (findstr ('<text:', char (scell))), sctype = STRING; endif + endif + switch sctype + case { FLOAT, CURRENCY, PERCENTAGE } + rawarr{ii, jj} = scell.getValue ().doubleValue (); + case BOOLEAN + rawarr {ii, jj} = scell.getValue () == 1; + case STRING + rawarr{ii, jj} = scell.getValue(); + case DATE + tmp = strsplit (char (scell.getValue ()), ' '); + yy = str2num (tmp{6}); + mo = find (ismember (months, toupper (tmp{2})) == 1); + dd = str2num (tmp{3}); + hh = str2num (tmp{4}(1:2)); + mi = str2num (tmp{4}(4:5)); + ss = str2num (tmp{4}(7:8)); + rawarr{ii, jj} = datenum (yy, mo, dd, hh, mi, ss); + case TIME + tmp = strsplit (char (scell.getValue ().getTime ()), ' '); + hh = str2num (tmp{4}(1:2)) / 24.0; + mi = str2num (tmp{4}(4:5)) / 1440.0; + ss = str2num (tmp{4}(7:8)) / 86600.0; + rawarr {ii, jj} = hh + mi + ss; + otherwise + # Nothing + endswitch + catch + # Probably a merged cell, just skip + # printf ("Error in row %d, col %d (addr. %s)\n", ii, jj, calccelladdress (lcol+jj-2, trow+ii-2)); + end_try_catch + endfor + endfor + else # ods,odfvsn == 3 # 1.2b2 for ii=1:nrows for jj = 1:ncols @@ -696,65 +747,11 @@ endfor endfor - else - # Version 1.2b3+ - for ii=1:nrows - for jj = 1:ncols - try - scell = sh.getCellAt (lcol+jj-2, trow+ii-2); - switch char (scell.getValueType ()) - case { FLOAT, CURRENCY, PERCENTAGE } - rawarr{ii, jj} = scell.getValue ().doubleValue (); - case BOOLEAN - rawarr {ii, jj} = scell.getValue () == 1; - case STRING - rawarr{ii, jj} = scell.getValue(); - case DATE - tmp = strsplit (char (scell.getValue ()), ' '); - yy = str2num (tmp{6}); - mo = find (ismember (months, toupper (tmp{2})) == 1); - dd = str2num (tmp{3}); - hh = str2num (tmp{4}(1:2)); - mi = str2num (tmp{4}(4:5)); - ss = str2num (tmp{4}(7:8)); - rawarr{ii, jj} = datenum (yy, mo, dd, hh, mi, ss); - case TIME - tmp = strsplit (char (scell.getValue ().getTime ()), ' '); - hh = str2num (tmp{4}(1:2)) / 24.0; - mi = str2num (tmp{4}(4:5)) / 1440.0; - ss = str2num (tmp{4}(7:8)) / 86600.0; - rawarr {ii, jj} = hh + mi + ss; - otherwise - # Nothing - endswitch - catch - # Probably a merged cell, just skip - end_try_catch - endfor - endfor endif + + # Keep track of data rectangle limits + ods.limits = [lcol, rcol; trow, brow]; - # Crop rawarr from all empty outer rows & columns just like Excel does - # & keep track of limits - emptr = cellfun ('isempty', rawarr); - if (all (all (emptr))) - rawarr = {}; - ods.limits= []; - else - 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 - # Crop textarray - rawarr = rawarr(irowt:irowb, icoll:icolr); - rstatus = 1; - ods.limits = [lcol+icoll-1, lcol+icolr-1; trow+irowt-1, trow+irowb-1]; - endif - rstatus = ~isempty (rawarr); endfunction Modified: trunk/octave-forge/main/io/inst/odsclose.m =================================================================== --- trunk/octave-forge/main/io/inst/odsclose.m 2010-11-14 16:53:26 UTC (rev 7915) +++ trunk/octave-forge/main/io/inst/odsclose.m 2010-11-14 16:55:15 UTC (rev 7916) @@ -1,4 +1,4 @@ -## Copyright (C) 2009 Philip Nienhuis <prnienhuis at users.sf.net> +## Copyright (C) 2009,2010 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 @@ -16,10 +16,14 @@ ## -*- texinfo -*- ## @deftypefn {Function File} [@var{ods}] = odsclose (@var{ods}) +## @deftypefnx {Function File} [@var{ods}] = odsclose (@var{ods}, @var{filename}) ## Close the OpenOffice_org Calc spreadsheet pointed to in struct -## @var{ods}, if needed write the file to disk. An empty pointer struct -## will be returned. odsclose will determine if the file must be written -## to disk based on information contained in @var{ods}. +## @var{ods}, if needed write the file to disk. +## odsclose will determine if the file must be written to disk based on +## information contained in @var{ods}. +## An empty pointer struct will be returned if no errors occurred. +## Optional argument @var{filename} can be used to write changed spreadsheet +## files to an other file than opened by odsopen(). ## ## You need the Java package > 1.2.6 plus odfdom.jar + xercesImpl.jar ## and/or jopendocument-<version>.jar installed on your computer + @@ -45,32 +49,64 @@ ## 2010-01-08 (OTK ODS write support) ## 2010-04-13 Improved help text a little bit ## 2010-08-25 Swapped in texinfo help text +## 2010-10-17 Fixed typo in error message about unknown interface +## 2010-10-27 Improved file change tracking tru ods.changed +## 2010-11-12 Keep ods file pointer when write errors occur. +## " Added optional filename arg to change filename to be written to -function [ ods ] = odsclose (ods) +function [ ods ] = odsclose (ods, filename=[]) # If needed warn that dangling spreadsheet pointers may be left if (nargout < 1) warning ("return argument missing - ods invocation not reset."); endif + if (~isempty (filename)) + if (ischar (filename)) + if (ods.changed == 0 || ods.changed > 2) + warning ("File %s wasn't changed, new filename ignored.", ods.filename); + else + if (strfind (tolower (filename), '.sxc') || strfind (tolower (filename), '.ods')) + ods.filename = filename; + else + error ('No .sxc or .ods filename extension specified'); + endif + endif + endif + endif + if (strcmp (ods.xtype, 'OTK')) # Java & ODF toolkit - if (ods.changed), ods.app.save (ods.filename); endif - ods.app.close (); + try + if (ods.changed && ods.changed < 3) + ods.app.save (ods.filename); + ods.changed = 0; + endif + ods.app.close (); + catch + end_try_catch elseif (strcmp (ods.xtype, 'JOD')) # Java & jOpenDocument - if (ods.changed) - ofile = java_new ('java.io.File', ods.filename); - ods.workbook.saveAs (ofile); - endif + try + if (ods.changed && ods.changed < 3) + ofile = java_new ('java.io.File', ods.filename); + ods.workbook.saveAs (ofile); + ods.changed = 0; + endif + catch + end_try_catch # elseif ---- < Other interfaces here > else - error (sprintf ("ods2oct: unknown OpenOffice.org .ods interface - %s.", ods.xtype)); + error (sprintf ("ods2close: unknown OpenOffice.org .ods interface - %s.", ods.xtype)); endif - # Reset file pointer - ods = []; + if (ods.changed && ods.changed < 3) + error ( sprintf ("Could not save file %s - read-only or in use elsewhere?\nFile pointer preserved", ods.filename)); + else + # Reset file pointer + ods = []; + endif endfunction Modified: trunk/octave-forge/main/io/inst/odsopen.m =================================================================== --- trunk/octave-forge/main/io/inst/odsopen.m 2010-11-14 16:53:26 UTC (rev 7915) +++ trunk/octave-forge/main/io/inst/odsopen.m 2010-11-14 16:55:15 UTC (rev 7916) @@ -1,4 +1,4 @@ -## Copyright (C) 2009 Philip Nienhuis <prnienhuis at users.sf.net> +## Copyright (C) 2009,2010 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 @@ -29,13 +29,13 @@ ## referred to as OTK and JOD, resp., and are preferred in that order by default ## (depending on their presence). ## -## @var{filename} must be a valid .ods OpenOffice.org file name. If @var{filename} -## does not contain any directory path, the file is saved in the current -## directory. +## @var{filename} must be a valid .ods OpenOffice.org file name including +## .ods suffix. If @var{filename} does not contain any directory path, +## the file is saved in the current directory. ## -## @var{readwrite} must be set to 1 if writing to spreadsheet is desired -## immediately after calling odsopen(). It merely serves proper handling -## of file errors (e.g., "file not found" or "new file created"). +## @var{readwrite} must be set to true ornumerical 1 if writing to spreadsheet +## is desired immediately after calling odsopen(). It merely serves proper +## handling of file errors (e.g., "file not found" or "new file created"). ## ## Optional input argument @var{reqintf} can be used to override the ODS ## interface automatically selected by odsopen. Currently implemented interfaces @@ -73,6 +73,9 @@ ## " Moved JOD version check to this func from subfunc getodsinterfaces() ## " Full support for odfdom 0.8.6 (in subfunc) ## 2010-08-27 Improved help text +## 2010-10-27 Improved tracking of file changes tru ods.changed +## 2010-11-12 Small changes to help text +## " Added try-catch to file open sections to create fallback to other intf ## ## Latest change on subfunction below: 2010-09-27 @@ -86,6 +89,10 @@ if (nargout < 1) usage ("ODS = odsopen (ODSfile, [Rw]). But no return argument specified!"); endif + ## FIXME: if ever another interface is implemented the below stanzas + ## should be remodeled after xlsopen() to allow for multiple + ## user-desired interface requests (for just 2 interfaces there's + ## no need yet) if (~isempty (reqinterface)) # Try to invoke requested interface for this call. Check if it # is supported anyway by emptying the corresponding var. @@ -112,15 +119,22 @@ if (rw) rw = 1; endif # Be sure it's either 0 or 1 initially - # Check if ODS file exists - fid = fopen (filename, 'rb'); + # Check if ODS file exists. Set open mode based on rw argument + if (rw), fmode = 'r+b'; else fmode = 'rb'; endif + fid = fopen (filename, fmode); if (fid < 0) - if (~rw) + if (~rw) # Read mode requested but file doesn't exist err_str = sprintf ("File %s not found\n", filename); error (err_str) - else - printf ("Creating file %s\n", filename); - rw = 2; + else # For writing we need more info: + fid = fopen (filename, 'rb'); # Check if it can be opened for reading + if (fid < 0) # Not found => create it + printf ("Creating file %s\n", filename); + rw = 3; + else # Found but not writable = error + fclose (fid); # Do not forget to close the handle neatly + error (sprintf ("Write mode requested but file %s is not writable\n", filename)) + endif endif else # close file anyway to avoid Java errors @@ -135,67 +149,95 @@ # Supported interfaces determined; now check ODS file type. chk1 = strcmp (tolower (filename(end-3:end)), '.ods'); - if (~chk1) - error ("Currently ods2oct can only read reliably from .ods files") - endif + # Only jOpenDocument (JOD) can read from .sxc files, but only if odfvsn = 2 + chk2 = strcmp (tolower (filename(end-3:end)), '.sxc'); +# if (~chk1) +# error ("Currently ods2oct can only read reliably from .ods files") +# endif ods = struct ("xtype", [], "app", [], "filename", [], "workbook", [], "changed", 0, "limits", [], "odfvsn", []); # Preferred interface = OTK (ODS toolkit & xerces), so it comes first. + # Keep track of which interface is selected. Can be used for fallback to other intf + odssupport = 0; - if (odsinterfaces.OTK) + if (odsinterfaces.OTK && ~odssupport) # Parts after user gfterry in # http://www.oooforum.org/forum/viewtopic.phtml?t=69060 odftk = 'org.odftoolkit.odfdom.doc'; - if (rw == 2) - # New spreadsheet - wb = java_invoke ([odftk '.OdfSpreadsheetDocument'], 'newSpreadsheetDocument'); - ods.changed = 2; - else - # Existing spreadsheet - wb = java_invoke ([odftk '.OdfDocument'], 'loadDocument', filename); - endif - ods.workbook = wb.getContentDom (); # Reads the entire spreadsheet - ods.xtype = 'OTK'; - ods.app = wb; - ods.filename = filename; - ods.odfvsn = odsinterfaces.odfvsn; + try + if (rw > 2) + # New spreadsheet + wb = java_invoke ([odftk '.OdfSpreadsheetDocument'], 'newSpreadsheetDocument'); + else + # Existing spreadsheet + wb = java_invoke ([odftk '.OdfDocument'], 'loadDocument', filename); + endif + ods.workbook = wb.getContentDom (); # Reads the entire spreadsheet + ods.xtype = 'OTK'; + ods.app = wb; + ods.filename = filename; + ods.odfvsn = odsinterfaces.odfvsn; + odssupport += 1; + catch + if (xlsinterfaces.JOD && ~rw && chk2) + printf ('Couldn''t open file %s using OTK; trying .sxc format with JOD...\n', filename); + else + error ('Couldn''t open file %s using OTK', filename); + endif + end_try_catch + endif - elseif (odsinterfaces.JOD) + if (odsinterfaces.JOD && ~odssupport) file = java_new ('java.io.File', filename); jopendoc = 'org.jopendocument.dom.spreadsheet.SpreadSheet'; - if (rw ==2) - # Create an empty 2 x 2 default TableModel template - tmodel= java_new ('javax.swing.table.DefaultTableModel', 2, 2); - wb = java_invoke (jopendoc, 'createEmpty', tmodel); - ods.changed = 2; - else - wb = java_invoke (jopendoc, 'createFromFile', file); - endif - ods.workbook = wb; - ods.filename = filename; - ods.xtype = 'JOD'; - ods.app = 'file'; - # Check jOpenDocument version - sh = ods.workbook.getSheet (0); - cl = sh.getCellAt (0, 0); try - # 1.2b3 has public getValueType () - cl.getValueType (); - ods.odfvsn = 3; + if (rw > 2) + # Create an empty 2 x 2 default TableModel template + tmodel= java_new ('javax.swing.table.DefaultTableModel', 2, 2); + wb = java_invoke (jopendoc, 'createEmpty', tmodel); + else + wb = java_invoke (jopendoc, 'createFromFile', file); + endif + ods.workbook = wb; + ods.filename = filename; + ods.xtype = 'JOD'; + ods.app = 'file'; + # Check jOpenDocument version. This can only work here when a + # workbook has been opened + sh = ods.workbook.getSheet (0); + cl = sh.getCellAt (0, 0); + try + # 1.2b3 has public getValueType () + cl.getValueType (); + ods.odfvsn = 3; + catch + # 1.2b2 has not + ods.odfvsn = 2; + printf ("NOTE: jOpenDocument v. 1.2b2 has limited functionality. Try upgrading to 1.2b3+\n"); + end_try_catch + odssupport += 2; catch - # 1.2b2 has not - ods.odfvsn = 2; - printf ("NOTE: jOpenDocument v. 1.2b2 has limited functionality. Try upgrading to 1.2b3+\n"); + error ('Couldn''t open file %s using JOD', filename); end_try_catch + endif -# elseif +# if # <other interfaces here> - else + if (~odssupport) warning ("No support for OpenOffice.org .ods I/O"); ods = []; + else + # From here on rw is tracked via ods.changed in the various lower + # level r/w routines and it is only used to determine if an informative + # message is to be given when saving a newly created ods file. + ods.changed = rw; + # Until something was written to existing files we keep status "unchanged". + # ods.changed = 0 (existing/only read from), 1 (existing/data added), 2 (new, + # data added) or 3 (pristine, no data added). + if (ods.changed == 1) ods.changed = 0; endif endif if (~isempty (reqinterface)) @@ -206,7 +248,7 @@ endfunction -## Copyright (C) 2009 Philip Nienhuis <prnienhuis at users.sf.net> +## Copyright (C) 2009,2010 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 @@ -257,6 +299,7 @@ ## 2010-09-11 Somewhat clarified messages about missing java classes ## " Rearranged code a bit; fixed typos in OTK detection code (odfdvsn -> odfvsn) ## 2010-09-27 More code cleanup +## 2010-11-12 Warning added about waning support for odfdom v. 0.7.5 function [odsinterfaces] = getodsinterfaces (odsinterfaces) @@ -273,6 +316,8 @@ # If we get here, at least Java works. Now check for proper entries # in class path. Under *nix the classpath must first be split up if (isunix) tmp1 = strsplit (char(tmp1), ":"); endif + ## FIXME implement more rigid Java version check a la xlsopen. + ## ods / Java stuff is less critical than xls / Java, however catch # No Java support odsinterfaces.OTK = 0; @@ -309,7 +354,10 @@ end_try_catch if ~(strcmp (odfvsn, '0.7.5') || strcmp (odfvsn, '0.8.6')) warning ("\nodfdom version %s is not supported - use v. 0.7.5 or 0.8.6.\n", odfvsn); - else + else + if (strcmp (odfvsn, '0.7.5')) + warning ("odfdom v. 0.7.5 support won't be maintained - please upgrade to 0.8.6 or higher."); + endif odsinterfaces.OTK = 1; printf (" Java/ODFtoolkit (OTK) OK. "); chk1 = 1; Modified: trunk/octave-forge/main/io/inst/odsread.m =================================================================== --- trunk/octave-forge/main/io/inst/odsread.m 2010-11-14 16:53:26 UTC (rev 7915) +++ trunk/octave-forge/main/io/inst/odsread.m 2010-11-14 16:55:15 UTC (rev 7916) @@ -1,4 +1,4 @@ -## Copyright (C) 2009 Philip Nienhuis <prnienhuis at users.sf.net> +## Copyright (C) 2009,2010 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 @@ -33,7 +33,8 @@ ##a struct containing the data origins of the various returned arrays. ## ## If @var{filename} does not contain any directory, the file is -## assumed to be in the current directory. +## assumed to be in the current directory. @var{filename} should include +## the filename extension (.ods). ## ## @var{wsh} is either numerical or text, in the latter case it is ## case-sensitive and it should conform to OpenOffice.org Calc sheet @@ -110,9 +111,18 @@ ## 2010-01-05 (....) ## 2010-03-04 Slight adaptations in texinfo ## 2010-05-31 Updated help text (delays i.c.o. empty range due to getusedrange call) +## 2010-11-10 Updated help text (filename extension req'd) +## 2010-11-13 Added some input validity checks function [ numarr, txtarr, rawarr, lim ] = odsread (filename, wsh=1, datrange=[], reqintf=[]) + if (nargin < 1 || isempty (findstr ('.ods', tolower (filename)))) + usage ("odsread: at least a filename incl. suffix is needed"); + endif + if (nargout < 1) + usage ("odsread: no output argument(s) specified"); + endif + ods = odsopen (filename, 0, reqintf); [rawarr, ods, rstatus] = ods2oct (ods, wsh, datrange); Modified: trunk/octave-forge/main/io/inst/odswrite.m =================================================================== --- trunk/octave-forge/main/io/inst/odswrite.m 2010-11-14 16:53:26 UTC (rev 7915) +++ trunk/octave-forge/main/io/inst/odswrite.m 2010-11-14 16:55:15 UTC (rev 7916) @@ -1,4 +1,4 @@ -## Copyright (C) 2009 Philip Nienhuis <pr.nienhuis at users.sf.net> +## Copyright (C) 2009,2010 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 @@ -24,9 +24,9 @@ ## ## @var{rstatus} returns 1 if write succeeded, 0 otherwise. ## -## @var{filename} must be a valid .ods OpenOffice.org file name. If -## @var{filename} does not contain any directory path, the file is saved -## in the current directory. +## @var{filename} must be a valid .ods OpenOffice.org file name (including +## file name extension). If @var{filename} does not contain any directory +## path, the file is saved in the current directory. ## ## @var{arr} can be any array type save complex. Mixed numeric/text arrays ## can only be cell arrays. @@ -86,13 +86,16 @@ ## 2010-01-14 Finalized write support tru ODS toolkit ## 2010-01-15 Added texinfo help ## 2010-08-25 Removed text about 31 char limit for sheet names (invalid) +## 2010-11-13 Added note about required file extension in help text +## 2010-11-13 Added some input arg checks function [ rstatus ] = odswrite (filename, data, wsh=1, range=[], reqintf=[]) - if (isnumeric (data)) - data = num2cell (data); - elseif (ischar (data)) - data = {data}; + # Input validity checks + if (nargin < 2) + usage ("Insufficient arguments - see 'help odswrite'"); + elseif (~ischar (filename) || isempty (findstr ('.ods', tolower (filename)))) + error ("First argument must be a filename (incl. .ods suffix)"); endif ods = odsopen (filename, 1, reqintf); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |