Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
typester committed Jul 25, 2012
0 parents commit 8eb0b70
Show file tree
Hide file tree
Showing 19 changed files with 747 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
@@ -0,0 +1,6 @@
Makefile
README
blib/
inc/
test.pl
private-*
4 changes: 4 additions & 0 deletions .shipit
@@ -0,0 +1,4 @@
steps = FindVersion, ChangeVersion, CheckChangeLog, DistTest, Commit, Tag, MakeDist, UploadCPAN

git.tagpattern = %v
git.push_to = origin
3 changes: 3 additions & 0 deletions .travis.yml
@@ -0,0 +1,3 @@
language: "perl"
before_install:
- perl Makefile.PL | cpanm
19 changes: 19 additions & 0 deletions MANIFEST.SKIP
@@ -0,0 +1,19 @@
\bRCS\b
\bCVS\b
^MANIFEST\.
^Makefile$
~$
\.old$
^blib/
^pm_to_blib
^MakeMaker-\d
\.gz$
\.cvsignore
^9\d_.*\.t
^\.git/
^\.shipit$
\.gitignore$
/\.git/
^\.gitmodules$
^\.travis.yml$
^README\.md$
38 changes: 38 additions & 0 deletions Makefile.PL
@@ -0,0 +1,38 @@
use strict;
use warnings;

BEGIN {
my @devmods = qw(
Module::Install::AuthorTests
Module::Install::ReadmeFromPod
Module::Install::Repository
);
my @not_available;

eval qq{use inc::Module::Install; 1;} or push @not_available, 'inc::Module::Install';
for my $mod (@devmods) {
eval qq{require $mod} or push @not_available, $mod;
}
if (@not_available) {
print qq{# The following modules are not available.\n};
print qq{# `$^X $0 | cpanm` will install them:\n};
print $_, "\n" for @not_available;
print "\n";
exit -1;
}
}

use inc::Module::Install;

name 'Data-XLSX-Parser';
all_from 'lib/Data/XLSX/Parser.pm';

readme_from 'lib/Data/XLSX/Parser.pm';
author_tests 'xt';
auto_set_repository;

requires 'Archive::Zip';
requires 'XML::Parser::Expat';
requires 'File::Temp';

WriteAll;
61 changes: 61 additions & 0 deletions lib/Data/XLSX/Parser.pm
@@ -0,0 +1,61 @@
package Data::XLSX::Parser;
use strict;
use warnings;

use Data::XLSX::Parser::DocumentArchive;
use Data::XLSX::Parser::Workbook;
use Data::XLSX::Parser::SharedStrings;
use Data::XLSX::Parser::Styles;
use Data::XLSX::Parser::Sheet;

sub new {
my ($class) = @_;

bless {
_row_event_handler => [],
_archive => undef,
_workbook => undef,
_shared_strings => undef,
}, $class;
}

sub add_row_event_handler {
my ($self, $handler) = @_;
push @{ $self->{_row_event_handler} }, $handler;
}

sub open {
my ($self, $file) = @_;
$self->{_archive} = Data::XLSX::Parser::DocumentArchive->new($file);
}

sub workbook {
my ($self) = @_;
$self->{_workbook} ||= Data::XLSX::Parser::Workbook->new($self->{_archive});
}

sub shared_strings {
my ($self) = @_;
$self->{_shared_strings} ||= Data::XLSX::Parser::SharedStrings->new($self->{_archive});
}

sub styles {
my ($self) = @_;
$self->{_styles} ||= Data::XLSX::Parser::Styles->new($self->{_archive});
}

sub sheet {
my ($self, $sheet_id) = @_;
$self->{_sheet} ||= Data::XLSX::Parser::Sheet->new($self, $self->{_archive}, $sheet_id);
}

sub _row_event {
my ($self, $row) = @_;

my $row_vals = [map { $_->{v} } @$row];
for my $handler (@{ $self->{_row_event_handler} }) {
$handler->($row_vals);
}
}

1;
40 changes: 40 additions & 0 deletions lib/Data/XLSX/Parser/DocumentArchive.pm
@@ -0,0 +1,40 @@
package Data::XLSX::Parser::DocumentArchive;
use strict;
use warnings;

use Archive::Zip;

sub new {
my ($class, $filename) = @_;

my $zip = Archive::Zip->new;
if ($zip->read($filename) != Archive::Zip::AZ_OK) {
die "Cannot open file: $filename";
}

bless {
_zip => $zip,
}, $class;
}

sub workbook {
my ($self) = @_;
$self->{_zip}->memberNamed('xl/workbook.xml');
}

sub sheet {
my ($self, $id) = @_;
$self->{_zip}->memberNamed(sprintf 'xl/worksheets/sheet%s.xml', $id);
}

sub shared_strings {
my ($self) = @_;
$self->{_zip}->memberNamed('xl/sharedStrings.xml');
}

sub styles {
my ($self) = @_;
$self->{_zip}->memberNamed('xl/styles.xml');
}

1;
66 changes: 66 additions & 0 deletions lib/Data/XLSX/Parser/SharedStrings.pm
@@ -0,0 +1,66 @@
package Data::XLSX::Parser::SharedStrings;
use strict;
use warnings;

use XML::Parser::Expat;
use Archive::Zip ();
use File::Temp;

sub new {
my ($class, $archive) = @_;

my $self = bless {
_data => [],

_is_string => 0,
_buf => '',
}, $class;

my $fh = File::Temp->new( SUFFIX => '.xml' );

my $handle = $archive->shared_strings or return $self;
die 'Failed to write temporally file: ', $fh->filename
unless $handle->extractToFileNamed($fh->filename) == Archive::Zip::AZ_OK;

my $parser = XML::Parser::Expat->new;
$parser->setHandlers(
Start => sub { $self->_start(@_) },
End => sub { $self->_end(@_) },
Char => sub { $self->_char(@_) },
);
$parser->parse($fh);

$self;
}

sub count {
my ($self) = @_;
scalar @{ $self->{_data} };
}

sub get {
my ($self, $index) = @_;
$self->{_data}->[$index];
}

sub _start {
my ($self, $parser, $name, %attrs) = @_;
$self->{_is_string} = 1 if $name eq 'si';
}

sub _end {
my ($self, $parser, $name) = @_;
$self->{_is_string} = 0;

if ($name eq 'si') {
push @{ $self->{_data} }, $self->{_buf};
$self->{_buf} = '';
}
}

sub _char {
my ($self, $parser, $data) = @_;
$self->{_buf} .= $data if $self->{_is_string};
}

1;

0 comments on commit 8eb0b70

Please sign in to comment.