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;
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',
103 predicate => 'has_language',
139 predicate => 'has_rank',
140 clearer => 'clear_rank',
143 ## For prefix/suffix readings
145 has 'join_prior' => (
149 writer => '_set_join_prior',
156 writer => '_set_join_next',
160 around BUILDARGS => sub {
170 # If one of our special booleans is set, we change the text and the
172 if( exists $args->{'is_lacuna'} && $args->{'is_lacuna'} && !exists $args->{'text'} ) {
173 $args->{'text'} = '#LACUNA#';
174 } elsif( exists $args->{'is_start'} && $args->{'is_start'} ) {
175 $args->{'id'} = '__START__'; # Change the ID to ensure we have only one
176 $args->{'text'} = '#START#';
178 } elsif( exists $args->{'is_end'} && $args->{'is_end'} ) {
179 $args->{'id'} = '__END__'; # Change the ID to ensure we have only one
180 $args->{'text'} = '#END#';
181 } elsif( exists $args->{'is_ph'} && $args->{'is_ph'} ) {
182 $args->{'text'} = $args->{'id'};
185 # Backwards compatibility for non-XMLname IDs
186 my $rid = $args->{'id'};
189 if( $rid !~ /^$xml10_namestartchar_rx/ ) {
192 $args->{'id'} = $rid;
194 $class->$orig( $args );
197 # Look for a lexeme-string argument in the build args; if there, pull in the
198 # morphology role if possible.
200 my( $self, $args ) = @_;
201 if( exists $args->{'lexemes'} ) {
202 unless( does_role( $self, 'Text::Tradition::Morphology' ) ) {
203 eval { apply_all_roles( $self, 'Text::Tradition::Morphology' ) };
205 warn "No morphology package installed; DROPPING lexemes";
209 $self->_deserialize_lexemes( $args->{'lexemes'} );
215 A meta attribute (ha ha), which should be true if any of our 'special'
216 booleans are true. Implies that the reading does not represent a bit
217 of text found in a witness.
223 return $self->is_start || $self->is_end || $self->is_lacuna || $self->is_ph;
226 =head2 is_identical( $other_reading )
228 Returns true if the reading is identical to the other reading. The basic test
229 is equality of ->text attributes, but this may be wrapped or overridden by
235 my( $self, $other ) = @_;
236 return $self->text eq $other->text;
241 Returns true if the reading may in theory be combined into a multi-reading
242 segment within the collation graph. The reading must not be a meta reading,
243 and it must not have any relationships in its own right with any others.
244 This test may be wrapped or overridden by extensions.
250 return undef if $self->is_meta;
251 return !$self->related_readings();
254 # Not really meant for public consumption. Adopt the text of the other reading
257 my( $self, $other, $joinstr ) = @_;
258 $self->alter_text( join( $joinstr, $self->text, $other->text ) );
259 # Change this reading to a joining one if necessary
260 $self->_set_join_next( $other->join_next );
263 =head1 Convenience methods
265 =head2 related_readings
267 Calls Collation's related_readings with $self as the first argument.
271 sub related_readings {
273 return $self->collation->related_readings( $self, @_ );
278 Calls Collation's reading_witnesses with $self as the first argument.
284 return $self->collation->reading_witnesses( $self, @_ );
289 Returns a list of Reading objects that immediately precede $self in the collation.
295 my @pred = $self->collation->sequence->predecessors( $self->id );
296 return map { $self->collation->reading( $_ ) } @pred;
301 Returns a list of Reading objects that immediately follow $self in the collation.
307 my @succ = $self->collation->sequence->successors( $self->id );
308 return map { $self->collation->reading( $_ ) } @succ;
324 Text::Tradition::Error->throw(
325 'ident' => 'Reading error',
331 __PACKAGE__->meta->make_immutable;