1 package Text::Tradition::Collation::Reading;
5 use Text::Tradition::Error;
7 use overload '""' => \&_stringify, 'fallback' => 1;
11 Text::Tradition::Collation::Reading - represents a reading (usually a word)
16 Text::Tradition is a library for representation and analysis of collated
17 texts, particularly medieval ones. A 'reading' refers to a unit of text,
18 usually a word, that appears in one or more witnesses (manuscripts) of the
19 tradition; the text of a given witness is composed of a set of readings in
26 Creates a new reading in the given collation with the given attributes.
31 =item collation - The Text::Tradition::Collation object to which this
32 reading belongs. Required.
34 =item id - A unique identifier for this reading. Required.
36 =item text - The word or other text of the reading.
38 =item is_start - The reading is the starting point for the collation.
40 =item is_end - The reading is the ending point for the collation.
42 =item is_lacuna - The 'reading' represents a known gap in the text.
44 =item is_ph - A temporary placeholder for apparatus parsing purposes. Do
45 not use unless you know what you are doing.
47 =item rank - The sequence number of the reading. This should probably not
52 One of 'text', 'is_start', 'is_end', or 'is_lacuna' is required.
68 Accessor methods for the given attributes.
74 isa => 'Text::Tradition::Collation',
89 writer => 'alter_text',
95 predicate => 'has_language',
131 predicate => 'has_rank',
132 clearer => 'clear_rank',
135 ## For morphological analysis
137 has 'normal_form' => (
140 predicate => 'has_normal_form',
143 # Holds the lexemes for the reading.
144 has 'reading_lexemes' => (
146 isa => 'ArrayRef[Text::Tradition::Collation::Reading::Lexeme]',
148 lexemes => 'elements',
149 has_lexemes => 'count',
150 clear_lexemes => 'clear',
151 add_lexeme => 'push',
153 default => sub { [] },
156 ## For prefix/suffix readings
158 has 'join_prior' => (
171 around BUILDARGS => sub {
181 # If one of our special booleans is set, we change the text and the
183 if( exists $args->{'is_lacuna'} && !exists $args->{'text'} ) {
184 $args->{'text'} = '#LACUNA#';
185 } elsif( exists $args->{'is_start'} ) {
186 $args->{'id'} = '#START#'; # Change the ID to ensure we have only one
187 $args->{'text'} = '#START#';
189 } elsif( exists $args->{'is_end'} ) {
190 $args->{'id'} = '#END#'; # Change the ID to ensure we have only one
191 $args->{'text'} = '#END#';
192 } elsif( exists $args->{'is_ph'} ) {
193 $args->{'text'} = $args->{'id'};
196 $class->$orig( $args );
199 # Look for a lexeme-string argument in the build args.
201 my( $self, $args ) = @_;
202 if( exists $args->{'lexemes'} ) {
203 $self->_deserialize_lexemes( $args->{'lexemes'} );
209 A meta attribute (ha ha), which should be true if any of our 'special'
210 booleans are true. Implies that the reading does not represent a bit
211 of text found in a witness.
217 return $self->is_start || $self->is_end || $self->is_lacuna || $self->is_ph;
220 =head1 Convenience methods
222 =head2 related_readings
224 Calls Collation's related_readings with $self as the first argument.
228 sub related_readings {
230 return $self->collation->related_readings( $self, @_ );
235 Calls Collation's reading_witnesses with $self as the first argument.
241 return $self->collation->reading_witnesses( $self, @_ );
246 Returns a list of Reading objects that immediately precede $self in the collation.
252 my @pred = $self->collation->sequence->predecessors( $self->id );
253 return map { $self->collation->reading( $_ ) } @pred;
258 Returns a list of Reading objects that immediately follow $self in the collation.
264 my @succ = $self->collation->sequence->successors( $self->id );
265 return map { $self->collation->reading( $_ ) } @succ;
268 =head2 set_identical( $other_reading)
270 Backwards compatibility method, to add a transposition relationship
271 between $self and $other_reading. Don't use this.
276 my( $self, $other ) = @_;
277 return $self->collation->add_relationship( $self, $other,
278 { 'type' => 'transposition' } );
288 Methods for the morphological information (if any) attached to readings.
289 A reading may be made up of multiple lexemes; the concatenated lexeme
290 strings ought to match the reading's normalized form.
292 See L<Text::Tradition::Collation::Reading::Lexeme> for more information
293 on Lexeme objects and their attributes.
297 Returns a true value if the reading has any attached lexemes.
301 Returns the Lexeme objects (if any) attached to the reading.
305 Wipes any associated Lexeme objects out of the reading.
307 =head2 add_lexeme( $lexobj )
309 Adds the Lexeme in $lexobj to the list of lexemes.
313 If the language of the reading is set, this method will use the appropriate
314 Language model to determine the lexemes that belong to this reading. See
315 L<Text::Tradition::lemmatize> if you wish to lemmatize an entire tradition.
321 unless( $self->has_language ) {
322 warn "Please set a language to lemmatize a tradition";
325 my $mod = "Text::Tradition::Language::" . $self->language;
327 $mod->can( 'reading_lookup' )->( $self );
331 # For graph serialization. Return a string representation of the associated
333 # TODO Push this in to the Lexeme package.
334 sub _serialize_lexemes {
337 foreach my $l ( $self->lexemes ) {
339 foreach my $wf ( $l->matching_forms ) {
340 push( @mf, $wf->to_string );
342 my $form = $l->form ? $l->form->to_string : '';
343 push( @lexstrs, join( '|L|', $l->language, $l->string, $form,
344 join( '|M|', @mf ) ) );
346 return join( '|R|', @lexstrs );
349 sub _deserialize_lexemes {
350 my( $self, $data ) = @_;
353 # Need to have the lexeme modules in order to have lexemes.
355 use Text::Tradition::Collation::Reading::Lexeme;
356 use Text::Tradition::Collation::Reading::WordForm;
360 # Good to go - add the lexemes.
362 foreach my $lexdata ( split( /\|R\|/, $data ) ) {
363 my( $lang, $lstring, $form, $allforms ) = split( /\|L\|/, $lexdata );
365 push( @wfdata, $form ) if $form;
366 push( @wfdata, split( /\|M\|/, $allforms ) );
368 foreach my $wd ( @wfdata ) {
369 my $wf = Text::Tradition::Collation::Reading::WordForm->new(
371 push( @wforms, $wf );
373 my %largs = ( 'language' => $lang, 'string' => $lstring );
375 $largs{'form'} = shift @wforms;
376 $largs{'is_disambiguated'} = 1;
378 $largs{'wordform_matchlist'} = \@wforms;
379 push( @lexemes, Text::Tradition::Collation::Reading::Lexeme->new( %largs ) );
381 $self->clear_lexemes;
382 $self->add_lexeme( @lexemes );
393 Text::Tradition::Error->throw(
394 'ident' => 'Reading error',
400 __PACKAGE__->meta->make_immutable;