lib/rspreadsheet/cell.rb in rspreadsheet-0.2.15 vs lib/rspreadsheet/cell.rb in rspreadsheet-0.3

- old
+ new

@@ -1,66 +1,73 @@ # @markup markdown # @author Jakub Tesinsky # @title rspreadsheet Cell require 'andand' -require 'rspreadsheet/xml_tied' +require 'rspreadsheet/xml_tied_item' require 'date' +require 'time' require 'bigdecimal' require 'bigdecimal/util' # for to_d method +require 'helpers/class_extensions' module Rspreadsheet +using ClassExtensions if RUBY_VERSION > '2.1' ### # Represents a cell in spreadsheet which has coordinates, contains value, formula and can be formated. # You can get this object like this (suppose that @worksheet contains {Rspreadsheet::Worksheet} object) # # @worksheet.cells(5,2) # # Note that when using syntax like `@worksheet[5,2]` or `@worksheet.B5` you won't get this object, but rather the value of the cell. +# More precisely it is equvalient to @worksheet.cells(5,2).value. +# class Cell < XMLTiedItem - attr_accessor :worksheet, :coli, :rowi - # `xml_options[:xml_items_node_name]` gives the name of the tag representing cell - # `xml_options[:number-columns-repeated]` gives the name of the previous tag which sais how many times the item is repeated - def xml_options; {:xml_items_node_name => 'table-cell', :xml_repeated_attribute => 'number-columns-repeated'} end + attr_accessor :worksheet + attr_reader :rowi + InternalDateFormat = '%Y-%m-%d' + InternalTimeFormat = 'PT%HH%MM%SS' +# InternalTimeFormat = 'PT%HH%MM%SS,%LS' - ## defining abstract methods from XMLTiedItem - # returns parent XMLTiedArray object of myself (XMLTiedItem) + # @!group XMLTiedItem related methods and extensions + def xml_options; {:xml_items_node_name => 'table-cell', :xml_repeated_attribute => 'number-columns-repeated'} end def parent; row end - def index; @coli end - def set_index(value); @coli=value end + def coli; index end + def set_rowi(arowi); @rowi = arowi end # this should ONLY be used by parent row def initialize(aworksheet,arowi,acoli) raise "First parameter should be Worksheet object not #{aworksheet.class}" unless aworksheet.kind_of?(Rspreadsheet::Worksheet) @worksheet = aworksheet @rowi = arowi - @coli = acoli + initialize_xml_tied_item(row,acoli) end def row; @worksheet.rows(rowi) end def coordinates; [rowi,coli] end def to_s; value.to_s end def valuexml; self.valuexmlnode.andand.inner_xml end - def valuexmlnode; self.xmlnode.children.first end + def valuexmlnode; self.xmlnode.elements.first end # use this to find node in cell xml. ex. xmlfind('.//text:a') finds all link nodes def valuexmlfindall(path) valuexmlnode.nil? ? [] : valuexmlnode.find(path) end def valuexmlfindfirst(path) valuexmlfindall(path).first end def inspect - "#<Rspreadsheet::Cell:Cell\n row:#{rowi}, col:#{coli} address:#{address}\n type: #{guess_cell_type.to_s}, value:#{value}\n mode: #{mode}\n>" + "#<Rspreadsheet::Cell\n row:#{rowi}, col:#{coli} address:#{address}\n type: #{guess_cell_type.to_s}, value:#{value}\n mode: #{mode}, format: #{format.inspect}\n>" end def value gt = guess_cell_type 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 == Date then Date.strptime(xmlnode.attributes['date-value'].to_s, InternalDateFormat) + when gt == Time then Time.strptime(xmlnode.attributes['time-value'].to_s, InternalTimeFormat) when gt == :percentage then xmlnode.attributes['value'].to_f when gt == :currency then xmlnode.attributes['value'].to_d end elsif self.mode == :outbound nil @@ -84,12 +91,18 @@ set_type_attribute('string') xmlnode << Tools.prepare_ns_node('text','p', avalue.to_s) when gt == Date then remove_all_value_attributes_and_content(xmlnode) set_type_attribute('date') - Tools.set_ns_attribute(xmlnode,'office','date-value', avalue.strftime('%Y-%m-%d')) - xmlnode << Tools.prepare_ns_node('text','p', avalue.strftime('%Y-%m-%d')) + avalue = avalue.strftime(InternalDateFormat) + Tools.set_ns_attribute(xmlnode,'office','date-value', avalue) + xmlnode << Tools.prepare_ns_node('text','p', avalue) + when gt == Time then + remove_all_value_attributes_and_content(xmlnode) + set_type_attribute('time') + Tools.set_ns_attribute(xmlnode,'office','time-value', avalue.strftime(InternalTimeFormat)) + xmlnode << Tools.prepare_ns_node('text','p', avalue.strftime('%H:%M')) when gt == :percentage then remove_all_value_attributes_and_content(xmlnode) set_type_attribute('percentage') Tools.set_ns_attribute(xmlnode,'office','value', '%0.2d%' % avalue.to_f) xmlnode << Tools.prepare_ns_node('text','p', (avalue.to_f*100).round.to_s+'%') @@ -103,10 +116,11 @@ Tools.set_ns_attribute(xmlnode,'calcext','value-type',typestring) end def remove_all_value_attributes_and_content(node=xmlnode) 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 + if att = Tools.get_ns_attribute(node, 'office','time-value') then att.remove! end if att = Tools.get_ns_attribute(node, 'table','formula') then att.remove! end node.content='' end def remove_all_type_attributes set_type_attribute(nil) @@ -118,10 +132,11 @@ gct = guess_cell_type case when gct == Float then :float when gct == String then :string when gct == Date then :date + when gct == Time then :time when gct == :percentage then :percentage when gct == :unassigned then :unassigned when gct == :currency then :currency when gct == NilClass then :empty when gct == nil then :unknown @@ -130,32 +145,33 @@ end def guess_cell_type(avalue=nil) # try guessing by value valueguess = case avalue when Numeric then Float + when Time then Time 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 from node xml + if valueguess.nil? # valueguess is most important 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 'time' then Time when 'date' then Date when 'percentage' then :percentage when 'N/A' then :unassigned when 'currency' then :currency else - if xmlnode.children.size == 0 + if xmlnode.elements.size == 0 nil else - 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}" + raise "Unknown type at #{coordinates.to_s} from #{xmlnode.to_s} / elements size=#{xmlnode.elements.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 @@ -207,10 +223,15 @@ remove_all_value_attributes_and_content(xmlnode) remove_all_type_attributes Tools.set_ns_attribute(xmlnode,'table','formula','of:'+formulastring.to_s) end def blank?; self.type==:empty or self.type==:unassigned end + + def border_top; format.border_top end + def border_right; format.border_right end + def border_bottom; format.border_bottom end + def border_left; format.border_left end end # proxy object to allow cell.format syntax. Also handles all logic for formats. # @private @@ -247,10 +268,11 @@ end Tools.set_ns_attribute(text_style_node,'fo',attribute_name,value) end def background_color=(value); set_cell_style_node_attribute('background-color', value) end def set_cell_style_node_attribute(attribute_name,value) + @cell.detach if @cell.mode != :regular if cell_style_node.nil? self.create_cell_style_node raise 'Style node was not correctly initialized' if cell_style_node.nil? end Tools.set_ns_attribute(cell_style_node,'fo',attribute_name,value) @@ -298,12 +320,81 @@ cellnode.doc.root.find("./office:automatic-styles#{xpath}").first end def currency Tools.get_ns_attribute_value(cellnode,'office','currency',nil) end + #returns object representing top border of the cell + def top; @top ||= Border.new(self,:top) end + def bottom; @bottom ||= Border.new(self,:bottom) end + def left; @left ||= Border.new(self,:left) end + def right; @right ||= Border.new(self,:right) end + alias :border_top :top + alias :border_right :right + alias :border_bottom :bottom + alias :border_left :left + + def inspect + "#<Rspreadsheet::CellFormat bold:#{bold?.inspect}, borders:#{top.get_value_string.inspect} #{right.get_value_string.inspect} #{bottom.get_value_string.inspect} #{left.get_value_string.inspect}>" + end + end +# represents one of the borders of a cell +class Border + def initialize(cellformat,side) + @cellformat = cellformat + @side = side.to_s + raise "Wrong side of border object, can be top, bottom, left or right" unless ['left','right','top','bottom'].include? @side + end + def cellnode; @cell.xmlnode end + def attribute_name; "border-#{@side}" end + + def width=(value); set_border_string_part(1, value) end + def style=(value); set_border_string_part(2, value.to_s) end + def color=(value); set_border_string_part(3, value) end + def width; get_border_string_part(1).to_f end + def style; get_border_string_part(2) end + def color; get_border_string_part(3) end + def delete + @cellformat.set_cell_style_node_attribute(attribute_name, 'none') + end + + + ## internals + + # set parth-th part of string which represents the border. String looks like "0.06pt solid #00ee00" + # part is 1 for width, 2 for style or 3 for color + def set_border_string_part(part,value) + current_value = @cellformat.get_cell_style_node_attribute(attribute_name) + + if current_value.nil? or (current_value=='none') + value_array = ['0.75pt', 'solid', '#000000'] # set default values + else + value_array = current_value.split(' ') + end + raise 'Strange border attribute value. Does not have 3 parts' unless value_array.length == 3 + value_array[part-1]=value + @cellformat.set_cell_style_node_attribute(attribute_name, value_array.join(' ')) + end + + def get_border_string_part(part) + current_value = @cellformat.get_cell_style_node_attribute(attribute_name) || @cellformat.get_cell_style_node_attribute('border') + if current_value.nil? or (current_value=='none') + return nil + else + value_array = current_value.split(' ') + raise 'Strange border attribute value. Does not have 3 parts' unless value_array.length == 3 + return value_array[part-1] + end + end + + def get_value_string + @cellformat.get_cell_style_node_attribute(attribute_name) + end + end + +end # module