lib/rspreadsheet/cell.rb in rspreadsheet-0.1.1 vs lib/rspreadsheet/cell.rb in rspreadsheet-0.2.0

- old
+ new

@@ -1,147 +1,83 @@ require 'andand' module Rspreadsheet -class Cell - attr_reader :col, :xmlnode - attr_reader :parent_row # for debug only - attr_accessor :mode - def self.empty_cell_node - LibXML::XML::Node.new('table-cell',nil, Tools.get_namespace('table')) +class RowOrNode + def mode + case + when xmlnode.nil? then :outbound + when repeated>1 then :repeated + else :regular + end end - def initialize(aparent_row,coli,asource_node=nil) - raise "First parameter should be Row object not #{aparent_row.class}" unless aparent_row.kind_of?(Rspreadsheet::Row) - @mode = :regular - @parent_row = aparent_row - if asource_node.nil? - asource_node = Cell.empty_cell_node - end - @xmlnode = asource_node - @col = coli +end + +class Cell < RowOrNode + attr_accessor :worksheet, :rowi, :coli + def initialize(aworksheet,arowi,acoli) + @worksheet = aworksheet + @rowi = arowi + @coli = acoli end - def ns_table; @parent_row.xmlnode.doc.root.namespaces.find_by_prefix('table') end - def ns_office; @parent_row.xmlnode.doc.root.namespaces.find_by_prefix('office') end - def ns_text; @parent_row.xmlnode.doc.root.namespaces.find_by_prefix('text') end - def to_s; value end - def cell_xml; self.xmlnode.to_s; end - def xml; self.xmlnode.children.first.andand.inner_xml end - def coordinates; [row,col]; end - def row; @parent_row.row; end - def worksheet; @parent_row.worksheet; end + def row; @worksheet.rows(rowi) end + def coordinates; [rowi,coli] end + + def xmlnode; @worksheet.cellxmlnode(@rowi,@coli) end def value gt = guess_cell_type - if (@mode == :regular) or (@mode == @repeated) + if (self.mode == :regular) or (self.mode == :repeated) case when gt == nil then nil - when gt == Float then @xmlnode.attributes['value'].to_f - when gt == String then @xmlnode.elements.first.andand.content.to_s - when gt == Date then Date.strptime(@xmlnode.attributes['date-value'].to_s, '%Y-%m-%d') - when gt == 'percentage' then @xmlnode.attributes['value'].to_f + when gt == Float then xmlnode.attributes['value'].to_f + when gt == String then xmlnode.elements.first.andand.content.to_s + when gt == Date then Date.strptime(xmlnode.attributes['date-value'].to_s, '%Y-%m-%d') + when gt == 'percentage' then xmlnode.attributes['value'].to_f end - elsif @mode == :outbound # needs to check whether it is still unbound - if parent_row.still_out_of_used_range? - nil - else - parent_row.normalize.cells(@col).value - end + elsif self.mode == :outbound + nil else - raise "Unknown cell mode #{@mode}" + raise "Unknown cell mode #{self.mode}" end end - def value=(avalue) - if @mode == :regular - gt = guess_cell_type(avalue) - case - when gt == nil then raise 'This value type is not storable to cell' - when gt == Float - set_type_attribute('float') - remove_all_value_attributes_and_content(@xmlnode) - Tools.set_ns_attribute(@xmlnode,'office','value', avalue.to_s) - @xmlnode << LibXML::XML::Node.new('p', avalue.to_f.to_s, ns_text) - when gt == String then - set_type_attribute('string') - remove_all_value_attributes_and_content(@xmlnode) - @xmlnode << LibXML::XML::Node.new('p', avalue.to_s, ns_text) - when gt == Date then - set_type_attribute('date') - remove_all_value_attributes_and_content(@xmlnode) - Tools.set_ns_attribute(@xmlnode,'office','date-value', avalue.strftime('%Y-%m-%d')) - @xmlnode << LibXML::XML::Node.new('p', avalue.strftime('%Y-%m-%d'), ns_text) - when gt == 'percentage' - set_type_attribute('float') - remove_all_value_attributes_and_content(@xmlnode) - Tools.set_ns_attribute(@xmlnode,'office','value', avalue.to_f.to_s) - @xmlnode << LibXML::XML::Node.new('p', (avalue.to_f*100).round.to_s+'%', ns_text) - end - elsif (@mode == :repeated) or (@mode == :outbound ) # Cell did not exist individually yet, detach row and create editable cell - row = @parent_row.detach - row.cells(@col).value = avalue - else - raise "Unknown cell mode #{@mode}" - end - end - def remove_all_value_attributes_and_content(node) - if att = Tools.get_ns_attribute(@xmlnode, 'office','value') then att.remove! end - if att = Tools.get_ns_attribute(@xmlnode, 'office','date-value') then att.remove! end - @xmlnode.content='' - end - def set_type_attribute(typestring) - Tools.set_ns_attribute(@xmlnode,'office','value-type',typestring) - end - def type - case - when guess_cell_type == Float then :float - when guess_cell_type == String then :string - when guess_cell_type == Date then :date - when guess_cell_type == 'percentage' then :percentage - when guess_cell_type == nil then :empty - else :unknown - end - end - # use this to find node in cell xml. ex. xmlfind('.//text:a') finds all link nodes - def xmlfindall(path) - xmlnode.find(path) - end - def xmlfindfirst(path) - xmlfindall(path).first - end - # based on @xmlnode and optionally value which is about to be assigned, guesses which type the result should be + def repeated; (Tools.get_ns_attribute_value(xmlnode, 'table', 'number-columns-repeated') || 1 ).to_i end def guess_cell_type(avalue=nil) # try guessing by value valueguess = case avalue when Numeric then Float when Date then Date when String,nil then nil else nil end result = valueguess - + if valueguess.nil? # valueguess is most important - # if not succesfull then try guessing by type - type = @xmlnode.attributes['value-type'].to_s - typeguess = case type + # if not succesfull then try guessing by type from node xml + typ = xmlnode.nil? ? 'N/A' : xmlnode.attributes['value-type'] + typeguess = case typ + when nil then nil when 'float' then Float when 'string' then String when 'date' then Date when 'percentage' then :percentage + when 'N/A' then :unassigned else - if @xmlnode.children.size == 0 + if xmlnode.children.size == 0 nil else - raise "Unknown type from #{@xmlnode.to_s} / children size=#{@xmlnode.children.size.to_s} / type=#{type}" + raise "Unknown type at #{coordinates.to_s} from #{xmlnode.to_s} / children size=#{xmlnode.children.size.to_s} / type=#{xmlnode.attributes['value-type'].to_s}" end end - + result = if !typeguess.nil? # if not certain by value, but have a typeguess if !avalue.nil? # with value we may try converting if (typeguess(avalue) rescue false) # if convertible then it is typeguess typeguess elsif (String(avalue) rescue false) # otherwise try string String - else # if not convertible to anyhing concious then nil + else # if not convertible to anything concious then nil nil end else # without value we just beleive typeguess typeguess end @@ -153,16 +89,114 @@ end end end result end - def inspect - "#<Cell:[#{row},#{col}]=#{value}(#{guess_cell_type.to_s})" + def detach_if_needed + if (self.mode == :repeated) or (self.mode == :outbound ) # Cell did not exist individually yet, detach row and create editable cell + @worksheet.detach_cell_in_xml(rowi,coli) + end + end + def value=(avalue) + detach_if_needed + if self.mode == :regular + gt = guess_cell_type(avalue) + case + when gt == nil then raise 'This value type is not storable to cell' + when gt == Float + set_type_attribute('float') + remove_all_value_attributes_and_content(xmlnode) + Tools.set_ns_attribute(xmlnode,'office','value', avalue.to_s) + xmlnode << LibXML::XML::Node.new('p', avalue.to_f.to_s, ns_text) + when gt == String then + set_type_attribute('string') + remove_all_value_attributes_and_content(xmlnode) + xmlnode << LibXML::XML::Node.new('p', avalue.to_s, ns_text) + when gt == Date then + set_type_attribute('date') + remove_all_value_attributes_and_content(xmlnode) + Tools.set_ns_attribute(xmlnode,'office','date-value', avalue.strftime('%Y-%m-%d')) + xmlnode << LibXML::XML::Node.new('p', avalue.strftime('%Y-%m-%d'), ns_text) + when gt == 'percentage' + set_type_attribute('float') + remove_all_value_attributes_and_content(xmlnode) + Tools.set_ns_attribute(xmlnode,'office','value', avalue.to_f.to_s) + xmlnode << LibXML::XML::Node.new('p', (avalue.to_f*100).round.to_s+'%', ns_text) + end + else + raise "Unknown cell mode #{self.mode}" + end end + def set_type_attribute(typestring) + Tools.set_ns_attribute(xmlnode,'office','value-type',typestring) + end + def remove_all_value_attributes_and_content(node) + if att = Tools.get_ns_attribute(node, 'office','value') then att.remove! end + if att = Tools.get_ns_attribute(node, 'office','date-value') then att.remove! end + node.content='' + end + def ns_table; xmlnode.doc.root.namespaces.find_by_prefix('table') end + def ns_office; xmlnode.doc.root.namespaces.find_by_prefix('office') end + def ns_text; xmlnode.doc.root.namespaces.find_by_prefix('text') end def relative(rowdiff,coldiff) - worksheet.cells(self.row+rowdiff, self.col+coldiff) + @worksheet.cells(self.rowi+rowdiff, self.coli+coldiff) end + def is_repeated?; mode == :repeated end + def type + gct = guess_cell_type + case + when gct == Float then :float + when gct == String then :string + when gct == Date then :date + when gct == :percentage then :percentage + when gct == :unassigned then :unassigned + when gct == nil then :unknown + else :unknown + end + end end + + +# class Cell +# attr_reader :col, :xmlnode +# attr_reader :parent_row # for debug only +# attr_accessor :mode +# def self.empty_cell_node +# LibXML::XML::Node.new('table-cell',nil, Tools.get_namespace('table')) +# end +# def initialize(aparent_row,coli,axmlnode=nil) +# raise "First parameter should be Row object not #{aparent_row.class}" unless aparent_row.kind_of?(Rspreadsheet::Row) +# @parent_row = aparent_row +# if axmlnode.nil? +# axmlnode = Cell.empty_cell_node +# end +# @xmlnode = axmlnode +# @col = coli +# # set @mode +# @mode = case +# when !@parent_row.used_col_range.include?(coli) then :outbound +# when Tools.get_ns_attribute_value(@xmlnode, 'table', 'number-columns-repeated').to_i>1 then :repeated +# else:regular +# end +# end +# def to_s; value end +# def cell_xml; self.xmlnode.to_s end +# def xml; self.xmlnode.children.first.andand.inner_xml end +# def address; Rspreadsheet::Tools.c2a(row,col) end +# def row; @parent_row.row end +# def worksheet; @parent_row.worksheet end +# # use this to find node in cell xml. ex. xmlfind('.//text:a') finds all link nodes +# def xmlfindall(path) +# xmlnode.find(path) +# end +# def xmlfindfirst(path) +# xmlfindall(path).first +# end +# # based on @xmlnode and optionally value which is about to be assigned, guesses which type the result should be +# def inspect +# "#<Rspreadsheet::Cell:Cell\n row:#{row}, col:#{col} address:#{address}\n type: #{guess_cell_type.to_s}, value:#{value}\n mode: #{mode}\n>" +# end +# end end