Add built local::lib
[catagits/Gitalist.git] / local-lib5 / lib / perl5 / Text / SimpleTable.pm
CommitLineData
3fea05b9 1# Copyright (C) 2005-2009, Sebastian Riedel.
2
3package Text::SimpleTable;
4
5use strict;
6use warnings;
7
8our $VERSION = '2.0';
9
10# Top
11our $TOP_LEFT = '.-';
12our $TOP_BORDER = '-';
13our $TOP_SEPARATOR = '-+-';
14our $TOP_RIGHT = '-.';
15
16# Middle
17our $MIDDLE_LEFT = '+-';
18our $MIDDLE_BORDER = '-';
19our $MIDDLE_SEPARATOR = '-+-';
20our $MIDDLE_RIGHT = '-+';
21
22# Left
23our $LEFT_BORDER = '| ';
24our $SEPARATOR = ' | ';
25our $RIGHT_BORDER = ' |';
26
27# Bottom
28our $BOTTOM_LEFT = "'-";
29our $BOTTOM_SEPARATOR = "-+-";
30our $BOTTOM_BORDER = '-';
31our $BOTTOM_RIGHT = "-'";
32
33# Wrapper
34our $WRAP = '-';
35
36sub new {
37 my ($class, @args) = @_;
38
39 # Instantiate
40 $class = ref $class || $class;
41 my $self = bless {}, $class;
42
43 # Columns and titles
44 my $cache = [];
45 my $max = 0;
46 for my $arg (@args) {
47 my $width;
48 my $name;
49
50 if (ref $arg) {
51 $width = $arg->[0];
52 $name = $arg->[1];
53 }
54 else { $width = $arg }
55
56 # Fix size
57 $width = 2 if $width < 2;
58
59 # Wrap
60 my $title = $name ? $self->_wrap($name, $width) : [];
61
62 # Column
63 my $col = [$width, [], $title];
64 $max = @{$col->[2]} if $max < @{$col->[2]};
65 push @$cache, $col;
66 }
67
68 # Padding
69 for my $col (@$cache) {
70 push @{$col->[2]}, '' while @{$col->[2]} < $max;
71 }
72 $self->{columns} = $cache;
73
74 return $self;
75}
76
77# The implementation is not very elegant, but gets the job done very well
78sub draw {
79 my $self = shift;
80
81 # Shortcut
82 return unless $self->{columns};
83
84 my $rows = @{$self->{columns}->[0]->[1]} - 1;
85 my $columns = @{$self->{columns}} - 1;
86 my $output = '';
87
88 # Top border
89 for my $j (0 .. $columns) {
90
91 my $column = $self->{columns}->[$j];
92 my $width = $column->[0];
93 my $text = $TOP_BORDER x $width;
94
95 if (($j == 0) && ($columns == 0)) {
96 $text = "$TOP_LEFT$text$TOP_RIGHT";
97 }
98 elsif ($j == 0) { $text = "$TOP_LEFT$text$TOP_SEPARATOR" }
99 elsif ($j == $columns) { $text = "$text$TOP_RIGHT" }
100 else { $text = "$text$TOP_SEPARATOR" }
101
102 $output .= $text;
103 }
104 $output .= "\n";
105
106 my $title = 0;
107 for my $column (@{$self->{columns}}) {
108 $title = @{$column->[2]} if $title < @{$column->[2]};
109 }
110
111 if ($title) {
112
113 # Titles
114 for my $i (0 .. $title - 1) {
115
116 for my $j (0 .. $columns) {
117
118 my $column = $self->{columns}->[$j];
119 my $width = $column->[0];
120 my $text = $column->[2]->[$i] || '';
121
122 $text = sprintf "%-${width}s", $text;
123
124 if (($j == 0) && ($columns == 0)) {
125 $text = "$LEFT_BORDER$text$RIGHT_BORDER";
126 }
127 elsif ($j == 0) { $text = "$LEFT_BORDER$text$SEPARATOR" }
128 elsif ($j == $columns) { $text = "$text$RIGHT_BORDER" }
129 else { $text = "$text$SEPARATOR" }
130
131 $output .= $text;
132 }
133
134 $output .= "\n";
135 }
136
137 # Title separator
138 $output .= $self->_draw_hr;
139
140 }
141
142 # Rows
143 for my $i (0 .. $rows) {
144
145 # Check for hr
146 if (!grep { defined $self->{columns}->[$_]->[1]->[$i] } 0 .. $columns)
147 {
148 $output .= $self->_draw_hr;
149 next;
150 }
151
152 for my $j (0 .. $columns) {
153
154 my $column = $self->{columns}->[$j];
155 my $width = $column->[0];
156 my $text = (defined $column->[1]->[$i]) ? $column->[1]->[$i] : '';
157
158 $text = sprintf "%-${width}s", $text;
159
160 if (($j == 0) && ($columns == 0)) {
161 $text = "$LEFT_BORDER$text$RIGHT_BORDER";
162 }
163 elsif ($j == 0) { $text = "$LEFT_BORDER$text$SEPARATOR" }
164 elsif ($j == $columns) { $text = "$text$RIGHT_BORDER" }
165 else { $text = "$text$SEPARATOR" }
166
167 $output .= $text;
168 }
169
170 $output .= "\n";
171 }
172
173 # Bottom border
174 for my $j (0 .. $columns) {
175
176 my $column = $self->{columns}->[$j];
177 my $width = $column->[0];
178 my $text = $BOTTOM_BORDER x $width;
179
180 if (($j == 0) && ($columns == 0)) {
181 $text = "$BOTTOM_LEFT$text$BOTTOM_RIGHT";
182 }
183 elsif ($j == 0) { $text = "$BOTTOM_LEFT$text$BOTTOM_SEPARATOR" }
184 elsif ($j == $columns) { $text = "$text$BOTTOM_RIGHT" }
185 else { $text = "$text$BOTTOM_SEPARATOR" }
186
187 $output .= $text;
188 }
189
190 $output .= "\n";
191
192 return $output;
193}
194
195sub hr {
196 my $self = shift;
197
198 for (0 .. @{$self->{columns}} - 1) {
199 push @{$self->{columns}->[$_]->[1]}, undef;
200 }
201
202 return $self;
203}
204
205sub row {
206 my ($self, @texts) = @_;
207 my $size = @{$self->{columns}} - 1;
208
209 # Shortcut
210 return $self if $size < 0;
211
212 for (1 .. $size) {
213 last if $size <= @texts;
214 push @texts, '';
215 }
216
217 my $cache = [];
218 my $max = 0;
219
220 for my $i (0 .. $size) {
221
222 my $text = shift @texts;
223 my $column = $self->{columns}->[$i];
224 my $width = $column->[0];
225 my $pieces = $self->_wrap($text, $width);
226
227 push @{$cache->[$i]}, @$pieces;
228 $max = @$pieces if @$pieces > $max;
229 }
230
231 for my $col (@{$cache}) { push @{$col}, '' while @{$col} < $max }
232
233 for my $i (0 .. $size) {
234 my $column = $self->{columns}->[$i];
235 my $store = $column->[1];
236 push @{$store}, @{$cache->[$i]};
237 }
238
239 return $self;
240}
241
242sub _draw_hr {
243 my $self = shift;
244 my $columns = @{$self->{columns}} - 1;
245 my $output = '';
246
247 for my $j (0 .. $columns) {
248
249 my $column = $self->{columns}->[$j];
250 my $width = $column->[0];
251 my $text = $MIDDLE_BORDER x $width;
252
253 if (($j == 0) && ($columns == 0)) {
254 $text = "$MIDDLE_LEFT$text$MIDDLE_RIGHT";
255 }
256 elsif ($j == 0) { $text = "$MIDDLE_LEFT$text$MIDDLE_SEPARATOR" }
257 elsif ($j == $columns) { $text = "$text$MIDDLE_RIGHT" }
258 else { $text = "$text$MIDDLE_SEPARATOR" }
259 $output .= $text;
260 }
261
262 $output .= "\n";
263
264 return $output;
265}
266
267# Wrap text
268sub _wrap {
269 my ($self, $text, $width) = @_;
270
271 my @cache;
272 my @parts = split "\n", $text;
273
274 for my $part (@parts) {
275
276 while (length $part > $width) {
277 my $subtext;
278 $subtext = substr $part, 0, $width - length($WRAP), '';
279 push @cache, "$subtext$WRAP";
280 }
281
282 push @cache, $part if defined $part;
283 }
284
285 return \@cache;
286}
287
2881;
289__END__
290
291=head1 NAME
292
293Text::SimpleTable - Simple Eyecandy ASCII Tables
294
295=head1 SYNOPSIS
296
297 use Text::SimpleTable;
298
299 my $t1 = Text::SimpleTable->new(5, 10);
300 $t1->row('foobarbaz', 'yadayadayada');
301 print $t1->draw;
302
303 .-------+------------.
304 | foob- | yadayaday- |
305 | arbaz | ada |
306 '-------+------------'
307
308 my $t2 = Text::SimpleTable->new([5, 'Foo'], [10, 'Bar']);
309 $t2->row('foobarbaz', 'yadayadayada');
310 $t2->row('barbarbarbarbar', 'yada');
311 print $t2->draw;
312
313 .-------+------------.
314 | Foo | Bar |
315 +-------+------------+
316 | foob- | yadayaday- |
317 | arbaz | ada |
318 | barb- | yada |
319 | arba- | |
320 | rbar- | |
321 | bar | |
322 '-------+------------'
323
324 my $t3 = Text::SimpleTable->new([5, 'Foo'], [10, 'Bar']);
325 $t3->row('foobarbaz', 'yadayadayada');
326 $t3->hr;
327 $t3->row('barbarbarbarbar', 'yada');
328 print $t3->draw;
329
330 .-------+------------.
331 | Foo | Bar |
332 +-------+------------+
333 | foob- | yadayaday- |
334 | arbaz | ada |
335 +-------+------------+
336 | barb- | yada |
337 | arba- | |
338 | rbar- | |
339 | bar | |
340 '-------+------------'
341
342=head1 DESCRIPTION
343
344Simple eyecandy ASCII tables.
345
346=head1 METHODS
347
348L<Text::SimpleTable> implements the following methods.
349
350=head2 C<new>
351
352 my $t = Text::SimpleTable->new(5, 10);
353 my $t = Text::SimpleTable->new([5, 'Col1', 10, 'Col2']);
354
355=head2 C<draw>
356
357 my $ascii = $t->draw;
358
359=head2 C<hr>
360
361 $t = $t->hr;
362
363=head2 C<row>
364
365 $t = $t->row('col1 data', 'col2 data');
366
367=head1 AUTHOR
368
369Sebastian Riedel, C<sri@cpan.org>.
370
371=head1 CREDITS
372
373In alphabetical order:
374
375Brian Cassidy
376
377=head1 COPYRIGHT AND LICENSE
378
379Copyright (C) 2005-2009, Sebastian Riedel.
380
381This program is free software, you can redistribute it and/or modify it under
382the terms of the Artistic License version 2.0.
383
384=cut