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',
110 predicate => 'has_language',
146 predicate => 'has_rank',
147 clearer => 'clear_rank',
150 ## For prefix/suffix readings
152 has 'join_prior' => (
156 writer => '_set_join_prior',
163 writer => '_set_join_next',
167 around BUILDARGS => sub {
177 # If one of our special booleans is set, we change the text and the
179 if( exists $args->{'is_lacuna'} && $args->{'is_lacuna'} && !exists $args->{'text'} ) {
180 $args->{'text'} = '#LACUNA#';
181 } elsif( exists $args->{'is_start'} && $args->{'is_start'} ) {
182 $args->{'id'} = '__START__'; # Change the ID to ensure we have only one
183 $args->{'text'} = '#START#';
185 } elsif( exists $args->{'is_end'} && $args->{'is_end'} ) {
186 $args->{'id'} = '__END__'; # Change the ID to ensure we have only one
187 $args->{'text'} = '#END#';
188 } elsif( exists $args->{'is_ph'} && $args->{'is_ph'} ) {
189 $args->{'text'} = $args->{'id'};
192 # Backwards compatibility for non-XMLname IDs
193 my $rid = $args->{'id'};
196 if( $rid !~ /^$xml10_namestartchar_rx/ ) {
199 $args->{'id'} = $rid;
201 $class->$orig( $args );
204 # Look for a lexeme-string argument in the build args; if there, pull in the
205 # morphology role if possible.
207 my( $self, $args ) = @_;
208 if( exists $args->{'lexemes'} ) {
209 unless( $self->can( '_deserialize_lexemes' ) ) {
210 warn "No morphology package installed; DROPPING lexemes";
213 $self->_deserialize_lexemes( $args->{'lexemes'} );
219 A meta attribute (ha ha), which should be true if any of our 'special'
220 booleans are true. Implies that the reading does not represent a bit
221 of text found in a witness.
227 return $self->is_start || $self->is_end || $self->is_lacuna || $self->is_ph;
230 =head2 is_identical( $other_reading )
232 Returns true if the reading is identical to the other reading. The basic test
233 is equality of ->text attributes, but this may be wrapped or overridden by
239 my( $self, $other ) = @_;
240 return $self->text eq $other->text;
245 Returns true if the reading may in theory be combined into a multi-reading
246 segment within the collation graph. The reading must not be a meta reading,
247 and it must not have any relationships in its own right with any others.
248 This test may be wrapped or overridden by extensions.
254 return undef if $self->is_meta;
255 return !$self->related_readings();
258 # Not really meant for public consumption. Adopt the text of the other reading
261 my( $self, $other, $joinstr ) = @_;
262 $self->alter_text( join( $joinstr, $self->text, $other->text ) );
263 # Change this reading to a joining one if necessary
264 $self->_set_join_next( $other->join_next );
267 =head1 Convenience methods
269 =head2 related_readings
271 Calls Collation's related_readings with $self as the first argument.
275 sub related_readings {
277 return $self->collation->related_readings( $self, @_ );
282 Calls Collation's reading_witnesses with $self as the first argument.
288 return $self->collation->reading_witnesses( $self, @_ );
293 Returns a list of Reading objects that immediately precede $self in the collation.
299 my @pred = $self->collation->sequence->predecessors( $self->id );
300 return map { $self->collation->reading( $_ ) } @pred;
305 Returns a list of Reading objects that immediately follow $self in the collation.
311 my @succ = $self->collation->sequence->successors( $self->id );
312 return map { $self->collation->reading( $_ ) } @succ;
328 Text::Tradition::Error->throw(
329 'ident' => 'Reading error',
335 __PACKAGE__->meta->make_immutable;