# # (C)2001-2002 Projecto Natura # package Biblio::Catalog::Simple; use Biblio::Catalog; # We need v5.6 for 'our' variables; require v5.6.0; use strict; use warnings; use DB_File; use Data::Dumper; use XML::DT; require Exporter; # Module Stuff our @ISA = qw(Exporter Biblio::Catalog); our %EXPORT_TAGS = ( 'all' => [ qw() ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw( &catalogNew &catalogLoad); # Version our $VERSION = '0.01'; ### # This function receives a filename and optionally an encoding. # Returns a catalog object and writes the basic skelleton to the # catalog file. # sub catalogNew { my $filename = shift; my $encoding = shift || ""; $encoding = " encoding=\"$encoding\"" if ($encoding ne ""); open CATALOG, ">$filename" or die "Can't open file $filename ($!)"; print CATALOG "\n"; print CATALOG "\n\n"; close CATALOG; return bless({ filename => $filename }); } ### # Given a filename, check if it is valid XML. If it is, return a new # catalog object. # sub catalogLoad { my ($filename) = @_; eval{dt($filename,())}; die "File $filename is not a valid catalog object ($@)" if $@; return bless({ filename => $filename }); } ### # This method should be called over a catalog object. The first # argument is the record identifier (like a key), the second is the # record contents and remaining arguments are an associative array # that maps relations names to lists of related terms or to a term # string where terms are separated by semi-colons. Adds the record # to the catalog cache (does not really save it!) # # If the record id already exists, it will be replaced. # sub catalogAdd { my ($self,$id,$xml,%relations) = @_; delete $self->{torem}{$id} if exists($self->{torem}{$id}); $self->{toadd}{$id}{xml} = $xml; if (%relations) { for (keys %relations) { if (ref($relations{$_}) eq "ARRAY") { push @{$self->{toadd}{$id}{rels}{$_}}, @{$relations{$_}}; } else { push @{$self->{toadd}{$id}{rels}{$_}}, split(/\s*;\s*/,$relations{$_}); } } } else { %{$self->{toadd}{$id}{rels}} = (); } return $id; } ### # Call this method with a list of record identifiers to be removed # from the catalog. # sub catalogRemove { my ($self, @records) = @_; for (@records) { delete ($self->{toadd}{$_}) if defined($self->{toadd}{$_}); $self->{torem}{$_} = 1; } } ### # Saves the catalog on disk flushing buffers. # sub catalogSave { my ($self) = @_; $/ = "\n"; my %cdata = (); my %rels = (); dt($self->{filename}, ( catalog => sub { "" }, entry => sub { $cdata{$v{id}}{xml} = $c; %{$cdata{$v{id}}{rels}} = %rels; %rels = (); }, rels => sub { "" }, rel => sub { push @{$rels{$v{type}}}, $c }, -default => sub {toxml} )); %cdata = %{ djoin(\%cdata,$self->{toadd}) }; for (keys %{$self->{torem}}) { delete $cdata{$_} if defined($cdata{$_}); } open CAT, $self->{filename} or die ("cannot open catalog file:$!"); chomp(my $fl = ); close CAT; $fl = '' unless ($fl =~ /<\?xml/); open CAT, ">$self->{filename}" or die ("Cannot open catalog file for writing: $!"); print CAT $fl,"\n"; for (keys %cdata) { print CAT "\n\n"; print CAT $cdata{$_}{xml}; if (keys %{$self->{toadd}{$_}{rels}} ) { print CAT "\n "; for my $k (sort keys %{$self->{toadd}{$_}{rels}}) { for my $data (sort @{$self->{toadd}{$_}{rels}{$k}}) { print CAT "\n $data"; } } print CAT "\n "; } print CAT "\n"; } print CAT "\n\n"; close CAT; } ### # Search method. Give an identifier and it will return the XML entry # sub catalogId { my ($self,$id) = @_; if (exists($self->{torem}{$id})) { return undef; } elsif (exists($self->{toadd}{$id})) { my $rels = ""; my @rs; if (@rs = keys %{$self->{toadd}{$id}{rels}}) { $rels.="\n "; for (@rs) { for my $data (@{$self->{toadd}{$id}{rels}{$_}}) { $rels.="\n $data"; } } $rels.="\n \n"; } return $self->{toadd}{$id}{xml}.$rels; } else { my $r = dt($self->{filename}, ( -default => sub{toxml}, catalog => sub{$c =~ s/^\s*|\s*$//g; $c}, entry => sub{ ($v{id} eq $id)?$c:"" }, ) ); return $r?$r:undef; } } # can't remember what this does... sub djoin { my ($a,$b) = @_; my $c = $a; for my $id (keys %{$b}) { if (defined($c->{$id})) { if ($b->{$id}{xml}) { $c->{$id} = $b->{$id}; } else { for (keys %{$b->{$id}{rels}}) { push @{$c->{$id}{rels}{$_}}, @{$b->{$id}{rels}{$_}}; } } } else { $c->{$id} = $b->{$id}; } } return $c; } 1; __END__ =head1 NAME Biblio::Catalog::Simple - Perl extension for managing XML catalog files =head1 SYNOPSIS use Biblio::Catalog::Simple; $catalog = catalogNew("catalogue.xml","ISO-8859-1"); $catalog = catalogLoad("catalogue.xml"); $catalog -> catalogAdd("2","Me"); $catalog -> catalogSave(); $catalog -> catalogId(4); # will return "They" # these two functions handle multi-language support @languages = catalogLanguages(); catalogSetLanguage('pt'); =head1 DESCRIPTION This module aims to help people who needs to manage a XML catalog. So, each record is identified by a number-id. The record contents should be correct XML accordingly with some DTD. =head2 catalogLanguages This function returns a list of the valid languages in the current version. =head2 catalogSetLanguage You must supply a valid language code (from the list returned by catalogLanguages) to change the language used in the forms and in the interactive shell. By default, it is used portuguese. =head2 catalogNew This function is an Catalog Object Constructor. Given a file name it creates an empty catalog and returns the correspondent object. If the second argument is present, it is used as a encoding reference. So, if you use C command, the file C will be created with the following contents: =head2 catalogLoad This is another Catalog Object Constructor. Really, it's an Object Re-Constructor as it loads a saved Catalog Object. It receives, as argument the catalog file name. =head2 catalogAdd A method to add a record to the catalog. The following arguments are the record id and the record contents. The record contents should be valid XML. Meanwhile, there is no need for a root tag, but it can exists. This XML is not checked, so, be sure it is valid XML. This method returns the record id. NOTE: the data is cached but not saved to the file. To have sure it is, really, saved, call the catalogSave method. If the id already exists, the contents will be replaced =head2 catalogSave This method syncs the catalog to disk. Use this everytime you make a big amount of changes on the catalog. =head2 catalogId Given an identifier, this method returns the corresponding value or undef if it does not exists. =head1 AUTHOR Alberto M. B. Simões =head1 SEE ALSO Manpages CGI(3) and perl(1). =cut