The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
package BibTeX::Parser::Entry;
{
  $BibTeX::Parser::Entry::VERSION = '0.69';
}

use warnings;
use strict;

use BibTeX::Parser;
use BibTeX::Parser::Author;



sub new {
	my ($class, $type, $key, $parse_ok, $fieldsref) = @_;

	my %fields = defined $fieldsref ? %$fieldsref : ();
	my $i=0;
	foreach my $field (keys %fields) {
	    if ($field !~ /^_/) {
		$fields{_fieldnums}->{$field}=$i;
		$i++;
	    }
	}
        if (defined $type) {
            $fields{_type} = uc($type);
        }
	$fields{_key}      = $key;
	$fields{_parse_ok} = $parse_ok;
        $fields{_raw}      = '';
	return bless \%fields, $class;
}



sub parse_ok {
	my $self = shift;
	if (@_) {
		$self->{_parse_ok} = shift;
	}
	$self->{_parse_ok};
}


sub error {
	my $self = shift;
	if (@_) {
		$self->{_error} = shift;
		$self->parse_ok(0);
	}
	return $self->parse_ok ? undef : $self->{_error};
}


sub type {
	if (scalar @_ == 1) {
		# get
		my $self = shift;
		return $self->{_type};
	} else {
		# set
		my ($self, $newval) = @_;
		$self->{_type} = uc($newval);
	}
}


sub key {
	if (scalar @_ == 1) {
		# get
		my $self = shift;
		return $self->{_key};
	} else {
		# set
		my ($self, $newval) = @_;
		$self->{_key} = $newval;
	}

}


sub field {
	if (scalar @_ == 2) {
		# get
		my ($self, $field) = @_;
		return $self->{ lc( $field ) };
	} else {
		my ($self, $key, $value) = @_;
		my $field = lc ($key);
		$self->{$field} = $value; #_sanitize_field($value);
		if (!exists($self->{_fieldnums}->{$field})) {
		    my $num = scalar keys %{$self->{_fieldnums}};
		    $self->{_fieldnums}->{$field} = $num;
		}
	}

}

use LaTeX::ToUnicode qw( convert );


sub cleaned_field {
        my ( $self, $field, @options ) = @_;
        if ( $field =~ /author|editor/i ) {
            return $self->field( $field );
        } else {
            return convert( $self->field( lc $field ), @options );
        }
}


sub cleaned_author {
    my $self = shift;
    $self->_handle_cleaned_author_editor( [ $self->author ], @_ );
}


sub cleaned_editor {
    my $self = shift;
    $self->_handle_cleaned_author_editor( [ $self->editor ], @_ );
}

sub _handle_cleaned_author_editor {
    my ( $self, $authors, @options ) = @_;
    map {
        my $author = $_;
        my $new_author = BibTeX::Parser::Author->new;
        map {
            $new_author->$_( convert( $author->$_, @options ) )
        } grep { defined $author->$_ } qw( first von last jr );
        $new_author;
    } @$authors;
}

no LaTeX::ToUnicode;

sub _handle_author_editor {
    my $type = shift;
    my $self = shift;
    if (@_) {
	if (@_ == 1) { #single string
	    # my @names = split /\s+and\s+/i, $_[0];
	    $_[0] =~ s/^\s*//; 
	    $_[0] =~ s/\s*$//; 
	    my @names = BibTeX::Parser::_split_braced_string($_[0], 
							     '\s+and\s+');
	    if (!scalar @names) {
		$self->error('Bad names in author/editor field');
		return;
	    }
	    $self->{"_$type"} = [map {new BibTeX::Parser::Author $_} @names];
	    $self->field($type, join " and ", @{$self->{"_$type"}});
	} else {
	    $self->{"_$type"} = [];
	    foreach my $param (@_) {
		if (ref $param eq "BibTeX::Author") {
		    push @{$self->{"_$type"}}, $param;
		} else {
		    push @{$self->{"_$type"}}, new BibTeX::Parser::Author $param;
		}
		
		$self->field($type, join " and ", @{$self->{"_$type"}});
	    }
	}
    } else {
	unless ( defined $self->{"_$type"}) {
	    my @names = BibTeX::Parser::_split_braced_string($self->{$type} || "", '\s+and\s+' );
	    $self->{"_$type"} = [map {new BibTeX::Parser::Author $_} @names];
	}
	return @{$self->{"_$type"}};
    }
}



sub author {
	_handle_author_editor('author', @_);
}


sub editor {
	_handle_author_editor('editor', @_);
}


sub fieldlist {
	my $self = shift;
	
	return grep {!/^_/} keys %$self;	
}


sub has {
	my ($self, $field) = @_;

	return defined $self->{$field};
}

sub _sanitize_field {
	my $value = shift;	
	for ($value) {
		tr/\{\}//d;
		s/\\(?!=[ \\])//g;
		s/\\\\/\\/g;
	}
	return $value;
}



sub raw_bibtex {
	my $self = shift;
	if (@_) {
		$self->{_raw} = shift;
	}
	return $self->{_raw};
}

sub to_string {
    my $self = shift;
    my %options=@_;
    if (!exists($options{canonize_names})) {
	$options{canonize_names}=1;
    }
    my @fields = grep {!/^_/} keys %$self;	
    @fields = sort {
	$self->{_fieldnums}->{$a} <=> 
	    $self->{_fieldnums}->{$b}} @fields;
    my $result = '@'.$self->type."{".$self->key.",\n";
    foreach my $field (@fields) {
	my $value = $self->field($field);
	if ($field eq 'author' && $options{canonize_names}) {
	    my @names = ($self->author);
	    $value = join(' and ', @names);
	}
	if ($field eq 'editor' && $options{canonize_names}) {
	    my @names = ($self->editors);
	    $value = join(' and ', @names);
	}
	$result .= "    $field = {"."$value"."},\n";
    }
    $result .= "}";
    return $result;
}

1; # End of BibTeX::Entry

__END__
=pod

=head1 NAME

BibTeX::Parser::Entry - Contains a single entry of a BibTeX document.

=head1 SYNOPSIS

This class ist a wrapper for a single BibTeX entry. It is usually created
by a BibTeX::Parser.

    use BibTeX::Parser::Entry;

    my $entry = BibTeX::Parser::Entry->new($type, $key, $parse_ok, \%fields);
    
    if ($entry->parse_ok) {
	    my $type    = $entry->type;
	    my $key     = $enty->key;
	    print $entry->field("title");
	    my @authors = $entry->author;
	    my @editors = $entry->editor;

	    ...

	    print $entry->to_string;
    }

   



=head1 FUNCTIONS

=head2 new

Create new entry.

=head2 parse_ok

If the entry was correctly parsed, this method returns a true value, false otherwise.

=head2 error

Return the error message, if the entry could not be parsed or undef otherwise.

=head2 type

Get or set the type of the entry, eg. 'ARTICLE' or 'BOOK'. Return value is 
always uppercase.

=head2 key

Get or set the reference key of the entry.

=head2 field($name [, $value])

Get or set the contents of a field. The first parameter is the name of the
field, the second (optional) value is the new value.

=head2 cleaned_field($name)

Retrieve the contents of a field in a format that is cleaned of TeX markup.

=head2 cleaned_author

Get an array of L<BibTeX::Parser::Author> objects for the authors of this
entry. Each name has been cleaned of accents and braces.

=head2 cleaned_editor

Get an array of L<BibTeX::Parser::Author> objects for the editors of this
entry. Each name has been cleaned of accents and braces.

=head2 author([@authors])

Get or set the authors. Returns an array of L<BibTeX::Author|BibTeX::Author> 
objects. The parameters can either be L<BibTeX::Author|BibTeX::Author> objects
or strings.

Note: You can also change the authors with $entry->field('author', $authors_string)

=head2 editor([@editors])

Get or set the editors. Returns an array of L<BibTeX::Author|BibTeX::Author> 
objects. The parameters can either be L<BibTeX::Author|BibTeX::Author> objects
or strings.

Note: You can also change the authors with $entry->field('editor', $editors_string)

=head2 fieldlist ()

Returns a list of all the fields used in this entry.

=head2 has($fieldname)

Returns a true value if this entry has a value for $fieldname.

=head2 raw_bibtex ()

Return raw BibTeX entry (if available).

=head2 to_string ([options])

Returns a text of the BibTeX entry in BibTeX format.  Options is
a hash.  Currently only the option C<canonize_names>
is supported.  If true (the default), authors' and editors' 
names are translated into canonical bibtex form.  The command 
C<$entry-E<gt>to_string(canonize_names=E<gt>0)> overrides this behavior.

=head1 VERSION

version 0.69


=head1 AUTHOR

Gerhard Gossen <gerhard.gossen@googlemail.com> and
Boris Veytsman <boris@varphi.com>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2013-2016 by Gerhard Gossen and Boris Veytsman

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut