1 package Text::Tradition::Collation::Reading;
4 use Moose::Util qw/ does_role apply_all_roles /;
5 use Moose::Util::TypeConstraints;
6 use Text::Tradition::Error;
7 use XML::Easy::Syntax qw( $xml10_name_rx $xml10_namestartchar_rx );
8 use overload '""' => \&_stringify, 'fallback' => 1;
12 where { $_ =~ /\A$xml10_name_rx\z/ },
13 message { 'Reading ID must be a valid XML attribute string' };
15 no Moose::Util::TypeConstraints;
17 # Enable plugin(s) if available
18 eval { with 'Text::Tradition::Morphology'; };
19 # Morphology package is not on CPAN, so don't warn of its absence
21 # warn "Text::Tradition::Morphology not found: $@. Disabling lexeme functionality";
26 Text::Tradition::Collation::Reading - represents a reading (usually a word)
31 Text::Tradition is a library for representation and analysis of collated
32 texts, particularly medieval ones. A 'reading' refers to a unit of text,
33 usually a word, that appears in one or more witnesses (manuscripts) of the
34 tradition; the text of a given witness is composed of a set of readings in
41 Creates a new reading in the given collation with the given attributes.
46 =item collation - The Text::Tradition::Collation object to which this
47 reading belongs. Required.
49 =item id - A unique identifier for this reading. Required.
51 =item text - The word or other text of the reading.
53 =item is_start - The reading is the starting point for the collation.
55 =item is_end - The reading is the ending point for the collation.
57 =item is_lacuna - The 'reading' represents a known gap in the text.
59 =item is_ph - A temporary placeholder for apparatus parsing purposes. Do
60 not use unless you know what you are doing.
62 =item rank - The sequence number of the reading. This should probably not
67 One of 'text', 'is_start', 'is_end', or 'is_lacuna' is required.
83 Accessor methods for the given attributes.
89 isa => 'Text::Tradition::Collation',
104 writer => 'alter_text',
111 writer => 'make_lemma',
147 predicate => 'has_rank',
148 clearer => 'clear_rank',
151 ## For prefix/suffix readings
153 has 'join_prior' => (
157 writer => '_set_join_prior',
164 writer => '_set_join_next',
168 around BUILDARGS => sub {
178 # If one of our special booleans is set, we change the text and the
180 if( exists $args->{'is_lacuna'} && $args->{'is_lacuna'} && !exists $args->{'text'} ) {
181 $args->{'text'} = '#LACUNA#';
182 } elsif( exists $args->{'is_start'} && $args->{'is_start'} ) {
183 $args->{'id'} = '__START__'; # Change the ID to ensure we have only one
184 $args->{'text'} = '#START#';
186 } elsif( exists $args->{'is_end'} && $args->{'is_end'} ) {
187 $args->{'id'} = '__END__'; # Change the ID to ensure we have only one
188 $args->{'text'} = '#END#';
189 } elsif( exists $args->{'is_ph'} && $args->{'is_ph'} ) {
190 $args->{'text'} = $args->{'id'};
193 # Backwards compatibility for non-XMLname IDs
194 my $rid = $args->{'id'};
197 if( $rid !~ /^$xml10_namestartchar_rx/ ) {
200 $args->{'id'} = $rid;
202 $class->$orig( $args );
205 # Look for a lexeme-string argument in the build args; if there, pull in the
206 # morphology role if possible.
208 my( $self, $args ) = @_;
209 if( exists $args->{'lexemes'} ) {
210 unless( $self->can( '_deserialize_lexemes' ) ) {
211 warn "No morphology package installed; DROPPING lexemes";
214 $self->_deserialize_lexemes( $args->{'lexemes'} );
220 A meta attribute (ha ha), which should be true if any of our 'special'
221 booleans are true. Implies that the reading does not represent a bit
222 of text found in a witness.
228 return $self->is_start || $self->is_end || $self->is_lacuna || $self->is_ph;
231 =head2 is_identical( $other_reading )
233 Returns true if the reading is identical to the other reading. The basic test
234 is equality of ->text attributes, but this may be wrapped or overridden by
240 my( $self, $other ) = @_;
241 return $self->text eq $other->text;
246 Returns true if the reading may in theory be combined into a multi-reading
247 segment within the collation graph. The reading must not be a meta reading,
248 and it must not have any relationships in its own right with any others.
249 This test may be wrapped or overridden by extensions.
255 return undef if $self->is_meta;
256 return !$self->related_readings();
259 # Not really meant for public consumption. Adopt the text of the other reading
262 my( $self, $other, $joinstr ) = @_;
263 $self->alter_text( join( $joinstr, $self->text, $other->text ) );
264 # Change this reading to a joining one if necessary
265 $self->_set_join_next( $other->join_next );
268 =head1 Convenience methods
270 =head2 related_readings
272 Calls Collation's related_readings with $self as the first argument.
276 sub related_readings {
278 return $self->collation->related_readings( $self, @_ );
283 Calls Collation's reading_witnesses with $self as the first argument.
289 return $self->collation->reading_witnesses( $self, @_ );
294 Returns a list of Reading objects that immediately precede $self in the collation.
300 my @pred = $self->collation->sequence->predecessors( $self->id );
301 return map { $self->collation->reading( $_ ) } @pred;
306 Returns a list of Reading objects that immediately follow $self in the collation.
312 my @succ = $self->collation->sequence->successors( $self->id );
313 return map { $self->collation->reading( $_ ) } @succ;
329 Text::Tradition::Error->throw(
330 'ident' => 'Reading error',
336 __PACKAGE__->meta->make_immutable;