jruby-poi 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +6 -0
- data/Gemfile.lock +25 -0
- data/README.markdown +16 -1
- data/Rakefile +22 -0
- data/VERSION +1 -1
- data/bin/autospec +16 -0
- data/bin/htmldiff +16 -0
- data/bin/ldiff +16 -0
- data/bin/rdebug +16 -0
- data/bin/rspec +16 -0
- data/jruby-poi.gemspec +55 -43
- data/lib/poi/workbook.rb +16 -0
- data/lib/poi/workbook/area.rb +60 -3
- data/lib/poi/workbook/cell.rb +83 -34
- data/lib/poi/workbook/named_range.rb +4 -4
- data/lib/poi/workbook/row.rb +20 -10
- data/lib/poi/workbook/workbook.rb +165 -19
- data/lib/poi/workbook/worksheet.rb +33 -15
- data/spec_debug.sh +13 -2
- data/specs/data/simple_with_picture.ods +0 -0
- data/specs/data/various_samples.xlsx +0 -0
- data/specs/spec_helper.rb +2 -1
- data/specs/support/matchers/cell_matcher.rb +1 -1
- data/specs/workbook_spec.rb +47 -10
- data/specs/writing_spec.rb +145 -0
- metadata +92 -66
- data/.gitignore +0 -9
@@ -8,19 +8,19 @@ module POI
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def name
|
11
|
-
@name.
|
11
|
+
@name.name_name
|
12
12
|
end
|
13
13
|
|
14
14
|
def sheet
|
15
|
-
@workbook[@name.
|
15
|
+
@workbook.worksheets[@name.sheet_name]
|
16
16
|
end
|
17
17
|
|
18
18
|
def formula
|
19
|
-
@name.
|
19
|
+
@name.refers_to_formula
|
20
20
|
end
|
21
21
|
|
22
22
|
def cells
|
23
|
-
[@workbook.cell(formula)].flatten
|
23
|
+
@name.is_deleted ? [] : [@workbook.cell(formula)].flatten
|
24
24
|
end
|
25
25
|
|
26
26
|
def values
|
data/lib/poi/workbook/row.rb
CHANGED
@@ -5,44 +5,54 @@ module POI
|
|
5
5
|
def initialize(worksheet)
|
6
6
|
@worksheet = worksheet
|
7
7
|
@poi_worksheet = worksheet.poi_worksheet
|
8
|
+
@rows = {}
|
8
9
|
end
|
9
10
|
|
10
11
|
def [](index)
|
11
|
-
Row.new(@poi_worksheet.
|
12
|
+
@rows[index] ||= Row.new(@poi_worksheet.row(index) || @poi_worksheet.create_row(index), @worksheet)
|
12
13
|
end
|
13
14
|
|
14
15
|
def size
|
15
|
-
@poi_worksheet.
|
16
|
+
@poi_worksheet.physical_number_of_rows
|
16
17
|
end
|
17
18
|
|
18
19
|
def each
|
19
|
-
it = @poi_worksheet.
|
20
|
-
yield Row.new(it.next) while it.
|
20
|
+
it = @poi_worksheet.row_iterator
|
21
|
+
yield Row.new(it.next, @worksheet) while it.has_next
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
24
|
-
class Row
|
25
|
-
def initialize(row)
|
26
|
-
@row
|
25
|
+
class Row < Facade(:poi_row, org.apache.poi.ss.usermodel.Row)
|
26
|
+
def initialize(row, worksheet)
|
27
|
+
@row = row
|
28
|
+
@worksheet = worksheet
|
27
29
|
end
|
28
30
|
|
29
31
|
def [](index)
|
30
32
|
return nil if poi_row.nil?
|
31
|
-
|
33
|
+
cells[index]
|
32
34
|
end
|
33
35
|
|
34
36
|
def cells
|
35
|
-
Cells.new(self)
|
37
|
+
@cells ||= Cells.new(self)
|
36
38
|
end
|
37
39
|
|
38
40
|
def index
|
39
41
|
return nil if poi_row.nil?
|
40
|
-
poi_row.
|
42
|
+
poi_row.row_num
|
41
43
|
end
|
42
44
|
|
45
|
+
def height_in_points= num
|
46
|
+
set_height_in_points num.to_f
|
47
|
+
end
|
48
|
+
|
43
49
|
def poi_row
|
44
50
|
@row
|
45
51
|
end
|
52
|
+
|
53
|
+
def worksheet
|
54
|
+
@worksheet
|
55
|
+
end
|
46
56
|
end
|
47
57
|
end
|
48
58
|
|
@@ -1,8 +1,18 @@
|
|
1
1
|
require 'tmpdir'
|
2
2
|
require 'stringio'
|
3
|
+
require 'java'
|
3
4
|
|
4
5
|
module POI
|
5
|
-
class Workbook
|
6
|
+
class Workbook < Facade(:poi_workbook, org.apache.poi.ss.usermodel.Workbook)
|
7
|
+
FONT = org.apache.poi.ss.usermodel.Font
|
8
|
+
FONT_CONSTANTS = Hash[*FONT.constants.map{|e| [e.downcase.to_sym, FONT.const_get(e)]}.flatten]
|
9
|
+
|
10
|
+
CELL_STYLE = org.apache.poi.ss.usermodel.CellStyle
|
11
|
+
CELL_STYLE_CONSTANTS = Hash[*CELL_STYLE.constants.map{|e| [e.downcase.to_sym, CELL_STYLE.const_get(e)]}.flatten]
|
12
|
+
|
13
|
+
INDEXED_COLORS = org.apache.poi.ss.usermodel.IndexedColors
|
14
|
+
INDEXED_COLORS_CONSTANTS = Hash[*INDEXED_COLORS.constants.map{|e| [e.downcase.to_sym, INDEXED_COLORS.const_get(e)]}.flatten]
|
15
|
+
|
6
16
|
def self.open(filename_or_stream)
|
7
17
|
name, stream = if filename_or_stream.kind_of?(java.io.InputStream)
|
8
18
|
[File.join(Dir.tmpdir, "spreadsheet.xlsx"), filename_or_stream]
|
@@ -21,11 +31,19 @@ module POI
|
|
21
31
|
instance
|
22
32
|
end
|
23
33
|
|
34
|
+
def self.create(filename)
|
35
|
+
self.new(filename, nil)
|
36
|
+
end
|
37
|
+
|
24
38
|
attr_reader :filename
|
25
39
|
|
26
40
|
def initialize(filename, io_stream)
|
27
41
|
@filename = filename
|
28
|
-
@workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(io_stream)
|
42
|
+
@workbook = io_stream ? org.apache.poi.ss.usermodel.WorkbookFactory.create(io_stream) : org.apache.poi.xssf.usermodel.XSSFWorkbook.new
|
43
|
+
end
|
44
|
+
|
45
|
+
def formula_evaluator
|
46
|
+
@formula_evaluator ||= @workbook.creation_helper.create_formula_evaluator
|
29
47
|
end
|
30
48
|
|
31
49
|
def save
|
@@ -48,32 +66,102 @@ module POI
|
|
48
66
|
def close
|
49
67
|
#noop
|
50
68
|
end
|
69
|
+
|
70
|
+
def create_sheet name='New Sheet'
|
71
|
+
# @workbook.createSheet name
|
72
|
+
worksheets[name]
|
73
|
+
end
|
74
|
+
|
75
|
+
def create_style options={}
|
76
|
+
font = @workbook.createFont
|
77
|
+
set_value( font, :font_height_in_points, options ) do | value |
|
78
|
+
value.to_i
|
79
|
+
end
|
80
|
+
set_value font, :bold_weight, options, FONT_CONSTANTS
|
81
|
+
set_value font, :color, options, INDEXED_COLORS_CONSTANTS do | value |
|
82
|
+
value.index
|
83
|
+
end
|
84
|
+
|
85
|
+
style = @workbook.createCellStyle
|
86
|
+
[:alignment, :vertical_alignment, :fill_pattern, :border_right, :border_left, :border_top, :border_bottom].each do | sym |
|
87
|
+
set_value style, sym, options, CELL_STYLE_CONSTANTS do | value |
|
88
|
+
value.to_i
|
89
|
+
end
|
90
|
+
end
|
91
|
+
[:right_border_color, :left_border_color, :top_border_color, :bottom_border_color, :fill_foreground_color, :fill_background_color].each do | sym |
|
92
|
+
set_value( style, sym, options, INDEXED_COLORS_CONSTANTS ) do | value |
|
93
|
+
value.index
|
94
|
+
end
|
95
|
+
end
|
96
|
+
[:hidden, :locked, :wrap_text].each do | sym |
|
97
|
+
set_value style, sym, options
|
98
|
+
end
|
99
|
+
[:rotation, :indentation].each do | sym |
|
100
|
+
set_value( style, sym, options ) do | value |
|
101
|
+
value.to_i
|
102
|
+
end
|
103
|
+
end
|
104
|
+
set_value( style, :data_format, options ) do |value|
|
105
|
+
@workbook.create_data_format.getFormat(value)
|
106
|
+
end
|
107
|
+
style.font = font
|
108
|
+
style
|
109
|
+
end
|
110
|
+
|
111
|
+
def set_value on, value_sym, from, using=nil
|
112
|
+
return on unless from.has_key?(value_sym)
|
113
|
+
value = if using
|
114
|
+
using[from[value_sym]]
|
115
|
+
else
|
116
|
+
from[value_sym]
|
117
|
+
end
|
118
|
+
value = yield value if block_given?
|
119
|
+
on.send("set_#{value_sym}", value)
|
120
|
+
on
|
121
|
+
end
|
51
122
|
|
52
123
|
def worksheets
|
53
124
|
@worksheets ||= Worksheets.new(self)
|
54
125
|
end
|
55
126
|
|
56
127
|
def named_ranges
|
57
|
-
@named_ranges ||= (0...@workbook.
|
58
|
-
NamedRange.new @workbook.
|
128
|
+
@named_ranges ||= (0...@workbook.number_of_names).collect do | idx |
|
129
|
+
NamedRange.new @workbook.name_at(idx), self
|
59
130
|
end
|
60
131
|
end
|
61
132
|
|
62
|
-
#
|
133
|
+
# reference can be a Fixnum, referring to the 0-based sheet or
|
63
134
|
# a String which is the sheet name or a cell reference.
|
64
135
|
#
|
65
136
|
# If a cell reference is passed the value of that cell is returned.
|
137
|
+
#
|
138
|
+
# If the reference refers to a contiguous range of cells an Array of values will be returned.
|
139
|
+
#
|
140
|
+
# If the reference refers to a multiple columns a Hash of values will be returned by column name.
|
66
141
|
def [](reference)
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
142
|
+
if Fixnum === reference
|
143
|
+
return worksheets[reference]
|
144
|
+
end
|
145
|
+
|
146
|
+
if sheet = worksheets.detect{|e| e.name == reference}
|
147
|
+
return sheet.poi_worksheet.nil? ? nil : sheet
|
148
|
+
end
|
149
|
+
|
150
|
+
cell = cell(reference)
|
151
|
+
if Array === cell
|
152
|
+
cell.collect{|e| e.value}
|
153
|
+
elsif Hash === cell
|
154
|
+
values = {}
|
155
|
+
cell.each_pair{|column_name, cells| values[column_name] = cells.collect{|e| e.value}}
|
156
|
+
values
|
157
|
+
else
|
158
|
+
cell.value
|
73
159
|
end
|
74
160
|
end
|
75
161
|
|
76
162
|
# takes a String in the form of a 3D cell reference and returns the Cell (eg. "Sheet 1!A1")
|
163
|
+
#
|
164
|
+
# If the reference refers to a contiguous range of cells an array of Cells will be returned
|
77
165
|
def cell reference
|
78
166
|
# if the reference is to a named range of cells, get that range and return it
|
79
167
|
if named_range = named_ranges.detect{|e| e.name == reference}
|
@@ -85,32 +173,90 @@ module POI
|
|
85
173
|
end
|
86
174
|
end
|
87
175
|
|
176
|
+
# check if the named_range is a full column reference
|
177
|
+
if column_reference?(named_range)
|
178
|
+
return all_cells_in_column named_range.formula
|
179
|
+
end
|
180
|
+
|
88
181
|
# if the reference is to an area of cells, get all the cells in that area and return them
|
89
182
|
cells = cells_in_area(reference)
|
90
183
|
unless cells.empty?
|
91
184
|
return cells.length == 1 ? cells.first : cells
|
92
185
|
end
|
93
186
|
|
94
|
-
|
95
|
-
|
187
|
+
if column_reference?(reference)
|
188
|
+
return all_cells_in_column reference
|
189
|
+
end
|
190
|
+
|
191
|
+
ref = POI::CELL_REF.new(reference)
|
192
|
+
single_cell ref
|
193
|
+
end
|
194
|
+
|
195
|
+
# ref is a POI::CELL_REF instance
|
196
|
+
def single_cell ref
|
197
|
+
if ref.sheet_name.nil?
|
96
198
|
raise 'cell references at the workbook level must include a sheet reference (eg. Sheet1!A1)'
|
97
199
|
else
|
98
|
-
worksheets[ref.
|
200
|
+
worksheets[ref.sheet_name][ref.row][ref.col]
|
99
201
|
end
|
100
202
|
end
|
101
203
|
|
102
204
|
def cells_in_area reference
|
103
205
|
area = Area.new(reference)
|
104
|
-
|
105
|
-
[]
|
106
|
-
else
|
107
|
-
area.in(self).compact
|
108
|
-
end
|
206
|
+
area.in(self)
|
109
207
|
end
|
110
208
|
|
111
209
|
def poi_workbook
|
112
210
|
@workbook
|
113
211
|
end
|
212
|
+
|
213
|
+
def on_update cell
|
214
|
+
#clear_all_formula_results
|
215
|
+
#formula_evaluator.notify_update_cell cell.poi_cell
|
216
|
+
end
|
217
|
+
|
218
|
+
def on_formula_update cell
|
219
|
+
#clear_all_formula_results
|
220
|
+
formula_evaluator.notify_set_formula cell.poi_cell
|
221
|
+
formula_evaluator.evaluate_formula_cell(cell.poi_cell)
|
222
|
+
end
|
223
|
+
|
224
|
+
def on_delete cell
|
225
|
+
#clear_all_formula_results
|
226
|
+
formula_evaluator.notify_delete_cell cell.poi_cell
|
227
|
+
end
|
228
|
+
|
229
|
+
def clear_all_formula_results
|
230
|
+
formula_evaluator.clear_all_cached_result_values
|
231
|
+
end
|
232
|
+
|
233
|
+
def all_cells_in_column reference
|
234
|
+
sheet_parts = reference.split('!')
|
235
|
+
area_parts = sheet_parts.last.split(':')
|
236
|
+
area_start = "#{sheet_parts.first}!#{area_parts.first}"
|
237
|
+
area_end = area_parts.last
|
238
|
+
|
239
|
+
area = org.apache.poi.ss.util.AreaReference.getWholeColumn(area_start, area_end)
|
240
|
+
full_ref = "#{area.first_cell.format_as_string}:#{area.last_cell.format_as_string}"
|
241
|
+
Area.new(full_ref).in(self)
|
242
|
+
# cell_reference = org.apache.poi.ss.util.CellReference.new( reference + "1" )
|
243
|
+
# column = cell_reference.get_col
|
244
|
+
# sheet = cell_reference.get_sheet_name
|
245
|
+
# worksheets[sheet].rows.collect{|row| row[column]}
|
246
|
+
end
|
247
|
+
|
248
|
+
private
|
249
|
+
def column_reference? named_range_or_reference
|
250
|
+
return false if named_range_or_reference.nil?
|
251
|
+
|
252
|
+
reference = named_range_or_reference
|
253
|
+
if NamedRange === named_range_or_reference
|
254
|
+
reference = named_range_or_reference.formula
|
255
|
+
end
|
256
|
+
cell_reference = reference.split('!', 2).last
|
257
|
+
beginning, ending = cell_reference.split(':')
|
258
|
+
!(beginning =~ /\d/ || (ending.nil? ? false : ending =~ /\d/))
|
259
|
+
end
|
114
260
|
end
|
115
261
|
end
|
116
262
|
|
@@ -7,54 +7,72 @@ module POI
|
|
7
7
|
@poi_workbook = workbook.poi_workbook
|
8
8
|
end
|
9
9
|
|
10
|
-
def [](
|
10
|
+
def [](index_or_name)
|
11
11
|
worksheet = case
|
12
|
-
when
|
13
|
-
@poi_workbook.
|
12
|
+
when index_or_name.kind_of?(Numeric)
|
13
|
+
@poi_workbook.sheet_at(index_or_name) || @poi_workbook.create_sheet
|
14
14
|
else
|
15
|
-
@poi_workbook.
|
15
|
+
@poi_workbook.get_sheet(index_or_name) || @poi_workbook.create_sheet(index_or_name)
|
16
16
|
end
|
17
|
-
Worksheet.new(worksheet)
|
17
|
+
Worksheet.new(worksheet, @workbook)
|
18
18
|
end
|
19
19
|
|
20
20
|
def size
|
21
|
-
@poi_workbook.
|
21
|
+
@poi_workbook.number_of_sheets
|
22
22
|
end
|
23
23
|
|
24
24
|
def each
|
25
|
-
(0...size).each { |i| yield Worksheet.new(@poi_workbook.
|
25
|
+
(0...size).each { |i| yield Worksheet.new(@poi_workbook.sheet_at(i), @workbook) }
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
class Worksheet
|
30
|
-
|
29
|
+
class Worksheet < Facade(:poi_worksheet, org.apache.poi.ss.usermodel.Sheet)
|
30
|
+
|
31
|
+
def initialize(worksheet, workbook)
|
31
32
|
@worksheet = worksheet
|
33
|
+
@workbook = workbook
|
32
34
|
end
|
33
|
-
|
35
|
+
|
34
36
|
def name
|
35
|
-
@worksheet.
|
37
|
+
@worksheet.sheet_name
|
38
|
+
end
|
39
|
+
|
40
|
+
def first_row
|
41
|
+
@worksheet.first_row_num
|
42
|
+
end
|
43
|
+
|
44
|
+
def last_row
|
45
|
+
@worksheet.last_row_num
|
36
46
|
end
|
37
47
|
|
38
48
|
def rows
|
39
|
-
Rows.new(self)
|
49
|
+
@rows ||= Rows.new(self)
|
40
50
|
end
|
41
51
|
|
42
52
|
# Accepts a Fixnum or a String as the row_index
|
43
53
|
#
|
44
|
-
# row_index as Fixnum
|
45
|
-
#
|
54
|
+
# row_index as Fixnum: returns the 0-based row
|
55
|
+
#
|
56
|
+
# row_index as String: assumes a cell reference within this sheet
|
57
|
+
# if the value of the reference is non-nil the value is returned,
|
58
|
+
# otherwise the referenced cell is returned
|
46
59
|
def [](row_index)
|
47
60
|
if Fixnum === row_index
|
48
61
|
rows[row_index]
|
49
62
|
else
|
50
63
|
ref = org.apache.poi.ss.util.CellReference.new(row_index)
|
51
|
-
rows[ref.
|
64
|
+
cell = rows[ref.row][ref.col]
|
65
|
+
cell && cell.value ? cell.value : cell
|
52
66
|
end
|
53
67
|
end
|
54
68
|
|
55
69
|
def poi_worksheet
|
56
70
|
@worksheet
|
57
71
|
end
|
72
|
+
|
73
|
+
def workbook
|
74
|
+
@workbook
|
75
|
+
end
|
58
76
|
end
|
59
77
|
end
|
60
78
|
|
data/spec_debug.sh
CHANGED
@@ -1,13 +1,24 @@
|
|
1
1
|
#!/bin/sh
|
2
2
|
#set -x
|
3
3
|
RUBY_DIR=$(dirname $(which ruby))/..
|
4
|
-
|
4
|
+
if [[ ${RUBY_DIR} == *1.6.* ]]
|
5
|
+
then
|
6
|
+
RUBYGEMS_DIR=${RUBY_DIR}/lib/ruby/gems/jruby/gems
|
7
|
+
else
|
8
|
+
RUBYGEMS_DIR=${RUBY_DIR}/lib/ruby/gems/1.8/gems
|
9
|
+
fi
|
5
10
|
|
6
11
|
GEM_COLUMNIZE=$(ls -1d $RUBYGEMS_DIR/columnize*/lib | head -1 | /usr/bin/ruby -e 'print File.expand_path($stdin.read)')
|
7
12
|
GEM_RUBY_DEBUG_BASE=$(ls -1d $RUBYGEMS_DIR/ruby-debug-base-*/lib | head -1 | /usr/bin/ruby -e 'print File.expand_path($stdin.read)')
|
8
13
|
GEM_RUBY_DEBUG_CLI=$(ls -1d $RUBYGEMS_DIR/ruby-debug-*/cli | head -1 | /usr/bin/ruby -e 'print File.expand_path($stdin.read)')
|
9
14
|
GEM_SOURCES=$(ls -1d $RUBYGEMS_DIR/sources-*/lib | head -1 | /usr/bin/ruby -e 'print File.expand_path($stdin.read)')
|
10
15
|
|
16
|
+
echo "RUBYGEMS_DIR: ${RUBYGEMS_DIR}"
|
17
|
+
echo "GEM_SOURCES: ${GEM_SOURCES}"
|
18
|
+
echo "GEM_COLUMNIZE: ${GEM_COLUMNIZE}"
|
19
|
+
echo "GEM_RUBY_DEBUG_CLI: ${GEM_RUBY_DEBUG_CLI}"
|
20
|
+
echo "GEM_RUBY_DEBUG_BASE: ${GEM_RUBY_DEBUG_BASE}"
|
21
|
+
|
11
22
|
runner="ruby --client \
|
12
23
|
-I${GEM_COLUMNIZE} \
|
13
24
|
-I${GEM_RUBY_DEBUG_BASE} \
|
@@ -15,7 +26,7 @@ runner="ruby --client \
|
|
15
26
|
-I${GEM_SOURCES} \
|
16
27
|
-rubygems -S"
|
17
28
|
|
18
|
-
cmd="
|
29
|
+
cmd="bundle exec rdebug rspec -c $*"
|
19
30
|
#cmd="irb"
|
20
31
|
|
21
32
|
$runner $cmd
|