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