1 package Net::Z3950::GRS1;
10 my ($class, $href, $map) = @_;
13 $self->{ELEMENTS} = [];
14 $self->{FH} = *STDOUT; ## Default output handle is STDOUT
17 if (defined($href) && ref($href) eq 'HASH') {
19 croak 'Usage: new Net::Z3950::GRS1($href, $map);';
21 $self->Hash2grs($href, $map);
29 my ($self, $href, $mapping) = @_;
35 $mapping = defined($mapping) ? $mapping : $self->{MAP};
36 $self->{MAP} = $mapping;
37 foreach $key (keys %$href) {
38 $content = $href->{$key};
39 next unless defined($content);
40 if (!defined($aref = $mapping->{$key})) {
41 print STDERR "Hash2grs: Unmapped key: '$key'\n";
44 if (ref($content) eq 'HASH') { ## Subtree?
45 my $subtree = new Net::Z3950::GRS1($content, $mapping);
46 $self->AddElement($aref->[0], $aref->[1], &Net::Z3950::GRS1::ElementData::Subtree, $subtree);
47 } elsif (!ref($content)) { ## Regular string?
48 $self->AddElement($aref->[0], $aref->[1], &Net::Z3950::GRS1::ElementData::String, $content);
49 } elsif (ref($content) eq 'ARRAY') {
50 my $issues = new Net::Z3950::GRS1;
51 foreach $issue (@$content) {
52 my $entry = new Net::Z3950::GRS1($issue, $mapping);
53 $issues->AddElement(5, 1, &Net::Z3950::GRS1::ElementData::Subtree, $entry);
55 $self->AddElement($aref->[0], $aref->[1], &Net::Z3950::GRS1::ElementData::Subtree, $issues);
57 print STDERR "Hash2grs: Unsupported content type\n";
67 return $self->{ELEMENTS};
71 sub CreateTaggedElement {
72 my ($self, $type, $value, $element_data) = @_;
75 $tagged->{TYPE} = $type;
76 $tagged->{VALUE} = $value;
77 $tagged->{OCCURANCE} = undef;
78 $tagged->{META} = undef;
79 $tagged->{VARIANT} = undef;
80 $tagged->{ELEMENTDATA} = $element_data;
87 my ($self, $TaggedElement) = @_;
89 return ($TaggedElement->{TYPE}, $TaggedElement->{VALUE});
94 my ($self, $TaggedElement) = @_;
96 return $TaggedElement->{ELEMENTDATA};
101 my ($self, $which, $content) = @_;
103 if ($which == &Net::Z3950::GRS1::ElementData::String) {
104 if (ref($content) eq '') {
107 croak "Wrong content type, expected a scalar";
109 } elsif ($which == &Net::Z3950::GRS1::ElementData::Subtree) {
110 if (ref($content) eq __PACKAGE__) {
113 croak "Wrong content type, expected a blessed reference";
116 croak "Content type currently not supported";
121 sub CreateElementData {
122 my ($self, $which, $content) = @_;
123 my $ElementData = {};
125 $self->CheckTypes($which, $content);
126 $ElementData->{WHICH} = $which;
127 $ElementData->{CONTENT} = $content;
134 my ($self, $type, $value, $which, $content) = @_;
135 my $Elements = $self->GetElementList;
136 my $ElmData = $self->CreateElementData($which, $content);
137 my $TaggedElm = $self->CreateTaggedElement($type, $value, $ElmData);
139 push(@$Elements, $TaggedElm);
144 my ($self, $level) = @_;
147 foreach (1..$level - 1) {
156 my ($self, $level, $pool, @args) = @_;
157 my $fh = $self->{FH};
158 my $str = sprintf($self->_Indent($level) . shift(@args), @args);
161 if (defined($pool)) {
170 FORMAT => &Net::Z3950::GRS1::Render::Plain,
176 my @Elements = @{$self->GetElementList};
178 my $fh = $args{HANDLE};
179 my $level = ++$args{LEVEL};
180 my $ref = $args{POOL};
182 if (!defined($fh) && defined($args{FILE})) {
183 open(FH, '> ' . $args{FILE}) or croak "Render: Unable to open file '$args{FILE}' for writing: $!";
187 $self->{FH} = defined($fh) ? $fh : $self->{FH};
189 foreach $TaggedElement (@Elements) {
190 my ($type, $value) = $self->GetTypeValue($TaggedElement);
191 if ($self->GetElementData($TaggedElement)->{WHICH} == &Net::Z3950::GRS1::ElementData::String) {
192 $self->_RecordLine($level, $ref, "(%s,%s) %s\n", $type, $value, $self->GetElementData($TaggedElement)->{CONTENT});
193 } elsif ($self->GetElementData($TaggedElement)->{WHICH} == &Net::Z3950::GRS1::ElementData::Subtree) {
194 $self->_RecordLine($level, $ref, "(%s,%s) {\n", $type, $value);
195 $self->GetElementData($TaggedElement)->{CONTENT}->Render(%args);
196 $self->_RecordLine($level, $ref, "}\n");
200 $self->_RecordLine($level, $ref, "(0,0)\n");
205 package Net::Z3950::GRS1::ElementData;
207 ## Define some constants according to the GRS-1 specification
214 sub TrueOrFalse { 6 }
217 sub ElementNotThere { 9 }
218 sub ElementEmpty { 10 }
219 sub NoDataRequested { 11 }
220 sub Diagnostic { 12 }
224 package Net::Z3950::GRS1::Render;
226 ## Define various types of rendering formats
240 Net::Z3950::Record::GRS1 - Perl package used to encode GRS-1 records.
244 use Net::Z3950::GRS1;
246 my $a_grs1_record = new Net::Z3950::Record::GRS1;
247 my $another_grs1_record = new Net::Z3950::Record::GRS1;
249 $a_grs1_record->AddElement($type, $value, $content);
250 $a_grs1_record->Render();
254 This Perl module helps you to create and manipulate GRS-1 records (generic record syntax).
255 So far, you have only access to three methods:
259 Creates a new GRS-1 object,
261 my $grs1 = new Net::Z3950::GRS1;
265 Lets you add entries to a GRS-1 object. The method should be called this way,
267 $grs1->AddElement($type, $value, $which, $content);
269 where $type should be an integer, and $value is free text. The $which argument should
270 contain one of the constants listed in Appendix A. Finally, $content contains the "thing"
271 that should be stored in this entry. The structure of $content should match the chosen
272 element data type. For
274 $which == Net::Z3950::GRS1::ElementData::String;
276 $content should be some kind of scalar. If on the other hand,
278 $which == Net::Z3950::GRS1::ElementData::Subtree;
280 $content should be a GRS1 object.
284 This method digs through the GRS-1 data structure and renders the record. You call it
289 If you want to access the rendered record through a variable, you can do it like this,
291 my $record_as_string;
292 $grs1->Render(POOL => \$record_as_string);
294 If you want it stored in a file, Render should be called this way,
296 $grs1->Render(FILE => 'record.grs1');
298 When no file name is specified, you can choose to stream the rendered record, for instance,
300 $grs1->Render(HANDLE => *STDOUT); ## or
301 $grs1->Render(HANDLE => *STDERR); ## or
302 $grs1->Render(HANDLE => *MY_HANDLE);
306 This method converts a hash into a GRS-1 object. Scalar entries within the hash are converted
307 into GRS-1 string elements. A hash entry can itself be a reference to another hash. In this case,
308 the new referenced hash will be converted into a GRS-1 subtree. The method is called this way,
310 $grs1->Hash2grs($href, $mapping);
312 where $href is the hash to be converted and $mapping is referenced hash specifying the mapping
313 between keys in $href and (type, value) pairs in the $grs1 object. The $mapping hash could
314 for instance look like this,
322 If the $grs1 object contains data prior to the invocation of Hash2grs, the new data represented
323 by the hash is simply added.
328 These element data types are specified in the Z39.50 protocol:
330 Net::Z3950::GRS1::ElementData::Octets
331 Net::Z3950::GRS1::ElementData::Numeric
332 Net::Z3950::GRS1::ElementData::Date
333 Net::Z3950::GRS1::ElementData::Ext
334 Net::Z3950::GRS1::ElementData::String <---
335 Net::Z3950::GRS1::ElementData::TrueOrFalse
336 Net::Z3950::GRS1::ElementData::OID
337 Net::Z3950::GRS1::ElementData::IntUnit
338 Net::Z3950::GRS1::ElementData::ElementNotThere
339 Net::Z3950::GRS1::ElementData::ElementEmpty
340 Net::Z3950::GRS1::ElementData::NoDataRequested
341 Net::Z3950::GRS1::ElementData::Diagnostic
342 Net::Z3950::GRS1::ElementData::Subtree <---
344 Only the '<---' marked types are so far supported in this package.
348 Anders Sønderberg Mortensen <sondberg@indexdata.dk>
349 Index Data ApS, Copenhagen, Denmark.
354 Specification of the GRS-1 standard, for instance in the Z39.50 protocol specification.
359 #Revision 1.5 2001-05-21 11:07:02 sondberg
360 #Extended maximum numbers of GRS-1 elements. Should be done dynamically.
362 #Revision 1.4 2001/05/17 14:07:06 sondberg
363 #Added some documentation.
365 #Revision 1.3 2001/05/17 13:43:04 sondberg
366 #Added method Hash2grs into GRS1 module.
368 #Revision 1.2 2001/03/13 14:53:15 sondberg
369 #Added a few lines of documentation into GRS1.pm.
371 #Revision 1.1 2001/03/13 14:17:15 sondberg
372 #Added support for GRS-1.