add support for alignment table input
[scpubgit/stemmatology.git] / lib / Text / Tradition / Collation / Reading.pm
CommitLineData
784877d9 1package Text::Tradition::Collation::Reading;
2
8e1394aa 3use Moose;
784877d9 4use MooseX::NonMoose;
4cdd82f1 5use Text::Tradition::Collation::Position;
784877d9 6
7extends 'Graph::Easy::Node';
8
784877d9 9has 'position' => (
d047cd52 10 is => 'rw',
4cdd82f1 11 isa => 'Text::Tradition::Collation::Position',
4a8828f0 12 predicate => 'has_position',
d047cd52 13 );
910a0a6d 14
15has 'rank' => (
16 is => 'rw',
17 isa => 'Int',
18 predicate => 'has_rank',
19 );
784877d9 20
21# This contains an array of reading objects; the array is a pool,
22# shared by the reading objects inside the pool. When a reading is
23# added to the pool, all the same_as attributes should be updated.
24has 'same_as' => (
d047cd52 25 is => 'rw',
26 isa => 'ArrayRef[Text::Tradition::Collation::Reading]',
27 );
784877d9 28
8e1394aa 29# Deal with the non-arg option for Graph::Easy's constructor.
30around BUILDARGS => sub {
31 my $orig = shift;
32 my $class = shift;
33
34 my %args;
35 if( @_ == 1 && ref( $_[0] ) ne 'HASH' ) {
36 return $class->$orig( 'name' => $_[0] );
37 } else {
38 return $class->$orig( @_ );
39 }
40};
41
4cdd82f1 42# Take constructor args as well as a Position argument.
43around position => sub {
44 my $orig = shift;
45 my $self = shift;
46 return $self->$orig() unless @_;
47
48 my @args = @_;
49 unless( @_ == 1 && ref( $_[0] ) eq 'Text::Tradition::Collation::Position' ) {
50 # We have constructor arguments; pass them to Position.
51 @args = ( Text::Tradition::Collation::Position->new( @_ ) );
52 }
53 $self->$orig( @args );
54};
55
d047cd52 56# Initialize the identity pool.
57sub BUILD {
58 my( $self, $args ) = @_;
8e1394aa 59 $self->same_as( [ $self ] );
d047cd52 60}
784877d9 61
e2902068 62sub text {
63 # Wrapper function around 'label' attribute.
64 my $self = shift;
65 if( @_ ) {
66 $self->set_attribute( 'label', $_[0] );
67 }
9463b0bf 68 return $self->label;
e2902068 69}
70
910a0a6d 71sub witnessed_by {
72 my( $self, $sigil, $backup ) = @_;
73 my @wits = $self->witnesses;
74 return 1 if grep { $_ eq $sigil } @wits;
75 if( $backup ) {
76 return 1 if grep { $_ eq $backup } @wits;
77 }
78 return 0;
79}
80
81sub witnesses {
82 my( $self ) = @_;
83 my @paths = grep { $_->get_attribute( 'class' ) eq 'path' } $self->outgoing;
84 push( @paths, grep { $_->get_attribute( 'class' ) eq 'path' } $self->incoming );
85 my %wits;
86 foreach my $p ( @paths ) {
87 if( $p->has_hidden_witnesses ) {
88 foreach ( @{$p->hidden_witnesses} ) {
89 $wits{$_} = 1;
90 }
91 } else {
92 $wits{$p->label} = 1;
93 }
94 }
95 return keys %wits;
96}
97
784877d9 98sub merge_from {
99 my( $self, $merged_node ) = @_;
100 # Adopt the identity pool of the other node.
101 my @now_identical = grep { $_ ne $merged_node } @{$merged_node->same_as};
102 my $new_pool = _merge_array_pool( \@now_identical, $self->same_as )
103 if @now_identical;
104
910a0a6d 105 # TODO Adopt the relationship attributes and segment memberships of the other node.
784877d9 106}
107
3265b0ce 108## Dealing with transposed readings. These methods are only really
109## applicable if we have a linear collation graph.
110
784877d9 111sub set_identical {
112 my( $self, $other_node ) = @_;
113 my $enlarged_pool = _merge_array_pool( $self->same_as,
114 $other_node->same_as );
115
116 # ...and set this node to point to the enlarged pool.
8e1394aa 117 $self->same_as( $enlarged_pool );
784877d9 118}
119
de51424a 120sub identical_readings {
121 my $self = shift;
122 my @same = grep { $_ ne $self } @{$self->same_as};
123 return @same;
124}
125
784877d9 126sub _merge_array_pool {
127 my( $pool, $main_pool ) = @_;
128 my %poolhash;
129 foreach ( @$main_pool ) {
130 # Note which nodes are already in the main pool so that we
131 # don't re-add them.
132 $poolhash{$_->name} = 1;
133 }
134
135 foreach( @$pool ) {
136 # Add the remaining nodes to the main pool...
137 push( @$main_pool, $_ ) unless $poolhash{$_->name};
138 }
139 return $main_pool;
140}
d047cd52 141
8e1394aa 142sub has_primary {
143 my $self = shift;
144 my $pool = $self->same_as;
df6d9812 145 return $pool->[0]->name ne $self->name;
8e1394aa 146}
147
148sub primary {
149 my $self = shift;
150 return $self->same_as->[0];
151}
152
4cdd82f1 153sub is_at_position {
154 my $self = shift;
155 return undef unless $self->has_position;
156 return $self->position->is_colocated( @_ );
157}
158
159# Returns all readings that adjoin this one on any path.
160sub neighbor_readings {
161 my( $self, $direction ) = @_;
162 $direction = 'both' unless $direction;
163 my @paths = grep { $_->isa( 'Text::Tradition::Collation::Path' ) } $self->edges;
164 my %connected;
165 foreach my $p ( @paths ) {
166 if( $p->to eq $self ) {
167 next if $direction eq 'forward';
168 $connected{$p->from->name} = $p->from;
169 } else { # $p->from eq $self
170 next if $direction =~ /^back/;
171 $connected{$p->to->name} = $p->to;
172 }
173 }
174 return values( %connected );
175}
176
910a0a6d 177# Returns all readings related to the one we've got.
178sub related_readings {
179 my( $self, $colocated ) = @_;
180 my @related;
181 foreach my $e ( $self->edges ) {
182 next unless $e->isa( 'Text::Tradition::Collation::Relationship' );
183 next if $colocated && $e->type eq 'repetition';
184 push( @related, $e->from eq $self ? $e->to : $e->from );
4cdd82f1 185 }
910a0a6d 186 return @related;
4cdd82f1 187}
188
3265b0ce 189## Keep track of which readings are unchanged across witnesses.
4a8828f0 190sub is_common {
191 my( $self ) = shift;
192 return $self->get_attribute( 'class' ) eq 'common';
193}
194
195sub make_common {
196 my( $self ) = shift;
197 $self->set_attribute( 'class', 'common' );
198}
199
200sub make_variant {
201 my( $self ) = shift;
202 $self->set_attribute( 'class', 'variant' );
203}
204
d047cd52 205no Moose;
206__PACKAGE__->meta->make_immutable;
207
2081;
209
210######################################################
211## copied from Graph::Easy::Parser docs
212######################################################
213# when overriding nodes, we also need ::Anon
214
215package Text::Tradition::Collation::Reading::Anon;
021bdbac 216use Moose;
217use MooseX::NonMoose;
218extends 'Text::Tradition::Collation::Reading';
219extends 'Graph::Easy::Node::Anon';
220no Moose;
221__PACKAGE__->meta->make_immutable;
d047cd52 222
021bdbac 2231;
224# use base qw/Text::Tradition::Collation::Reading/;
225# use base qw/Graph::Easy::Node::Anon/;
d047cd52 226
227######################################################
228# and :::Empty
229
230package Text::Tradition::Collation::Reading::Empty;
021bdbac 231use Moose;
232use MooseX::NonMoose;
233extends 'Graph::Easy::Node::Empty';
234no Moose;
235__PACKAGE__->meta->make_immutable;
d047cd52 236
021bdbac 2371;
238# use base qw/Text::Tradition::Collation::Reading/;
d047cd52 239
240######################################################