1 package Text::Tradition::Collation::Reading;
4 use Moose::Util qw/ does_role apply_all_roles /;
5 use Text::Tradition::Datatypes;
6 use Text::Tradition::Error;
7 use XML::Easy::Syntax qw( $xml10_name_rx $xml10_namestartchar_rx );
8 use overload '""' => \&_stringify, 'fallback' => 1;
10 # Enable plugin(s) if available
11 eval { with 'Text::Tradition::Morphology'; };
12 # Morphology package is not on CPAN, so don't warn of its absence
14 # warn "Text::Tradition::Morphology not found: $@. Disabling lexeme functionality";
19 Text::Tradition::Collation::Reading - represents a reading (usually a word)
24 Text::Tradition is a library for representation and analysis of collated
25 texts, particularly medieval ones. A 'reading' refers to a unit of text,
26 usually a word, that appears in one or more witnesses (manuscripts) of the
27 tradition; the text of a given witness is composed of a set of readings in
34 Creates a new reading in the given collation with the given attributes.
39 =item collation - The Text::Tradition::Collation object to which this
40 reading belongs. Required.
42 =item id - A unique identifier for this reading. Required.
44 =item text - The word or other text of the reading.
46 =item is_start - The reading is the starting point for the collation.
48 =item is_end - The reading is the ending point for the collation.
50 =item is_lacuna - The 'reading' represents a known gap in the text.
52 =item is_ph - A temporary placeholder for apparatus parsing purposes. Do
53 not use unless you know what you are doing.
55 =item rank - The sequence number of the reading. This should probably not
60 One of 'text', 'is_start', 'is_end', or 'is_lacuna' is required.
76 Accessor methods for the given attributes.
82 isa => 'Text::Tradition::Collation',
97 writer => 'alter_text',
104 writer => 'make_lemma',
140 predicate => 'has_rank',
141 clearer => 'clear_rank',
144 ## For prefix/suffix readings
146 has 'join_prior' => (
150 writer => '_set_join_prior',
157 writer => '_set_join_next',
161 around BUILDARGS => sub {
171 # If one of our special booleans is set, we change the text and the
173 if( exists $args->{'is_lacuna'} && $args->{'is_lacuna'} && !exists $args->{'text'} ) {
174 $args->{'text'} = '#LACUNA#';
175 } elsif( exists $args->{'is_start'} && $args->{'is_start'} ) {
176 $args->{'id'} = '__START__'; # Change the ID to ensure we have only one
177 $args->{'text'} = '#START#';
179 } elsif( exists $args->{'is_end'} && $args->{'is_end'} ) {
180 $args->{'id'} = '__END__'; # Change the ID to ensure we have only one
181 $args->{'text'} = '#END#';
182 } elsif( exists $args->{'is_ph'} && $args->{'is_ph'} ) {
183 $args->{'text'} = $args->{'id'};
186 # Backwards compatibility for non-XMLname IDs
187 my $rid = $args->{'id'};
190 if( $rid !~ /^$xml10_namestartchar_rx/ ) {
193 $args->{'id'} = $rid;
195 $class->$orig( $args );
198 # Look for a lexeme-string argument in the build args; if there, pull in the
199 # morphology role if possible.
201 my( $self, $args ) = @_;
202 if( exists $args->{'lexemes'} ) {
203 unless( $self->can( '_deserialize_lexemes' ) ) {
204 warn "No morphology package installed; DROPPING lexemes";
207 $self->_deserialize_lexemes( $args->{'lexemes'} );
213 A meta attribute (ha ha), which should be true if any of our 'special'
214 booleans are true. Implies that the reading does not represent a bit
215 of text found in a witness.
221 return $self->is_start || $self->is_end || $self->is_lacuna || $self->is_ph;
224 =head2 is_identical( $other_reading )
226 Returns true if the reading is identical to the other reading. The basic test
227 is equality of ->text attributes, but this may be wrapped or overridden by
233 my( $self, $other ) = @_;
234 return $self->text eq $other->text;
239 Returns true if the reading may in theory be combined into a multi-reading
240 segment within the collation graph. The reading must not be a meta reading,
241 and it must not have any relationships in its own right with any others.
242 This test may be wrapped or overridden by extensions.
248 return undef if $self->is_meta;
249 return !$self->related_readings();
252 # Not really meant for public consumption. Adopt the text of the other reading
255 my( $self, $other, $joinstr ) = @_;
256 $self->alter_text( join( $joinstr, $self->text, $other->text ) );
257 # Change this reading to a joining one if necessary
258 $self->_set_join_next( $other->join_next );
261 =head1 Convenience methods
263 =head2 related_readings
265 Calls Collation's related_readings with $self as the first argument.
269 sub related_readings {
271 return $self->collation->related_readings( $self, @_ );
276 Calls Collation's reading_witnesses with $self as the first argument.
282 return $self->collation->reading_witnesses( $self, @_ );
287 Returns a list of Reading objects that immediately precede $self in the collation.
293 my @pred = $self->collation->sequence->predecessors( $self->id );
294 return map { $self->collation->reading( $_ ) } @pred;
299 Returns a list of Reading objects that immediately follow $self in the collation.
305 my @succ = $self->collation->sequence->successors( $self->id );
306 return map { $self->collation->reading( $_ ) } @succ;
322 Text::Tradition::Error->throw(
323 'ident' => 'Reading error',
329 __PACKAGE__->meta->make_immutable;