package AMF::Record;

###  AMF::Record is an identified noun
###

=head1 NAME

AMF::Record -- AMF record objects hold AMF data records.  An AMF record is an
AMF noun with id attribute.

=head1 SYNOPSIS

=head2 The main thing, accessing the data

  ... 
  $rec = amf_get_next_record;

  my $id  = $rec -> id;
  my $ty  = $tec -> type;

  if ( $ty eq 'person' ) {
    my $name      = $rec -> get_value( 'name' );   ## name adjective's value, 
                                                   ## only the 1st occurence
    my @affils    = $rec -> get_value( 'ispartof/name'   );
    my @urls      = $rec -> get_value( 'isauthorof/file/url' );

    ##  attribute of isauthor elements
    my @verb_attr = $rec -> get_value( 'isauthorof/@attr' );  

    ##  attribute id of nouns, enclosed in isauthor elements
    my @work_ids  = $rec -> get_value( 'isauthorof/ID'  );    

    ...

  } elsif ( $ty eq 'text' ) {
    my $title     = $rec -> get_value( 'title' );
    my $type      = $rec -> get_value( 'type' ); ## not the same as $ty

    ...
  }

or access directly the data structure:

  my $title_val  = $rec ->{title}[0][0];  ###  adjectives
  my $title_lang = $rec ->{title}[1]{'xml:lang'};

  my $files      = $rec ->{file};
  my $file_url_1st_val  = $rec ->{file}[0][0] {url}[0][0];
  my $file_url_1st_lang = $rec ->{file}[0][1] {'xml:lang'};

  foreach ( @$files ) {
    my $cont = $_ ->[0];
    my $attr = $_ ->[1];

    my $urls = $cont ->{url};

    foreach ( @$urls ) {
      my $value = $_ ->[0];
      my $lang  = $_ ->[1] {'xml:lang'};
      ...
    } 
    ...
  }

or the compromise between the two, get values B<and> their attributes:

  my @abstracts = $rec -> get_value_wattr( 'abstract' );
  foreach ( @abstracts ) {
    my $value      = $_ ->[0];
    my $attributes = $_ ->[1];
    my $lang       = $attributes ->{'xml:lang'};
  }

  my @dates     = $rec -> get_value_wattr( 'date' );
  foreach ( @dates ) {
    my $date       = $_ ->[0];
    my $event      = $_ ->[1] ->{event};
  }


=head2 Other methods

Get MD5 checksum of the record.  Likely to change if the record changes,
likely to stay the same if the record stays the same.  (Slow.)

  my $checksum = $rec ->md5checksum;

Get AMF/XML string representation of the record:

  my $xml  = $rec -> stringify;


Data creation methods:

  my $new  = AMF::Record -> new( ID => 'GFIO:ZXCVBN', TYPE => 'text' );
  $new -> adjective( 'title', { 'xml:lang' => 'en' }, "New AMF text noun" );
  $new -> verb( "hasauthor", {}, $person );



=head1 DESCRIPTION

AMF::Record objects are produced and returned by AMF::Parser, when you parse a
piece of AMF data.  This class gives you an interface to access its contents.

=head2 METHODS

=head3 C<-E<gt> id>

Returns record's id if it is defined.

=head3 C<-E<gt> ref>

Returns record's ref attribute value if it is defined.

=head3 C<-E<gt> type>

Returns the noun type.  Values: C<"person", "organization", "text",
"collection">.

=head3 C<-E<gt> get_value( SPEC )>

Extract something from the record.  What particular to extract is specified by
the SPEC.  SPEC is a string in an XPath-inspired simple format.  It contsists
of one or more steps.  Each step is either an adjective name, a verb name, an
adjective container element name, or an attribute spec.  Steps are joined with
C<"/"> into SPEC.  Attribute spec may only happen as the last step of a SPEC.

SPEC examples: C<"name", "haspart", "haspart/title", "haspart/file",
"haspart/file/url", "haspart/@id", "haspart/@ref", "haspart/title/@xml:lang">,

In scalar context returns first match if anything found.  In list context
returns list of matched values.  When your spec pointed to a verb (as in
C<"haspart">) or an adjective container (C<"file">), and so we have to return
you a noun or an adjective container, you will accordingly get respective
objects in your return list.  It will be objects of class AMF::Noun or
L<AMF::AdjContainer>.  They support the same interface, so you can do this:

  my @parts = $rec -> get_value( "haspart" );
  foreach ( @parts ) {
    my $part = $_; 
    my $type  = $part -> type;
    my $title = $part -> get_value( "title" );
    ...
  }


Attributes C<id> and C<ref> of an AMF noun and its type (element name) can be
accessed as adjectives C<ID>, C<REF> and C<TYPE> respectively:

  my $id = $rec -> get_value( "ID" );              # same as $rec->id;
  my @re = $rec -> get_value( "isauthorof/REF"  );
  my @ty = $rec -> get_value( "isauthorof/TYPE" );


=head3 C<-E<gt> get_value_wattr( SPEC )>

Like get_value(), but for each match returns an arrayref.  (Multiple arrayrefs
if there were several matches.)  In each of the returned array, first position
is for the actual value (string in case of adjectives, object in case of verbs
and adjective containers).  Second position is a hash of attributes, if any.

See example above, in the SYNOPSIS section, where I extract event attribute
from the date adjectives.


=head3 C<<< -> stringify >>>

Reconstruct the record's AMF/XML representation.  It will spit out invalid AMF
if the data structure you call it on does not conform to the AMF spec.  Will
do necessary XML escaping for your data.

To be valid AMF, the return value of C<stringify()> neccessarily has to be
wrapped into an C<amf> element:

  <amf xmlns='http://amf.openlib.org'
       xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
       xsi:schemaLocation=
  'http://amf.openlib.org http://amf.openlib.org/2001/amf.xsd'>

     ...

  </amf>

=head2 Object data structure

Examples in SYNOPSIS shall give you some clues, S<C<amfch -d>> shall help even more.

=head1 SEE ALSO

L<AMF::Parser>, L<amfch> utility, test files in t/ and L<AMF::AdjContainer>

=head1 AUTHOR

Ivan Kurmanov, http://www.ahinea.com/en/
for ACIS project, http://acis.openlib.org/

=cut


use strict;
use warnings;
use AMF::Noun;

use vars qw( @ISA );
@ISA = qw( AMF::Noun );

sub new {
  my $class = shift;
  my %para  = @_;
  
  if ( not $para{ID} 
       and not $para{ATTR}{id}
     ) {
    $class = 'AMF::Noun';
  }
  my $self = $class -> SUPER::new( @_ );
}



1;


