use Moose;
use MooseX::NonMoose;
+use Text::Tradition::Collation::Position;
extends 'Graph::Easy::Node';
-subtype 'Position'
- => as 'Str',
- => where { $_ =~ /^\d+\,\d+$/ },
- message { 'Position must be of the form x,y' };
-
has 'position' => (
- is => 'rw',
- isa => 'Position',
- );
+ is => 'rw',
+ isa => 'Text::Tradition::Collation::Position',
+ predicate => 'has_position',
+ );
+
+has 'rank' => (
+ is => 'rw',
+ isa => 'Int',
+ predicate => 'has_rank',
+ );
+
+has 'is_lacuna' => (
+ is => 'rw',
+ isa => 'Bool',
+ );
# This contains an array of reading objects; the array is a pool,
# shared by the reading objects inside the pool. When a reading is
# added to the pool, all the same_as attributes should be updated.
has 'same_as' => (
- is => 'rw',
- isa => 'ArrayRef[Text::Tradition::Collation::Reading]',
- default => [ $self ],
- );
-
-# This is a hash mapping of 'relationship => reading'.
-# TODO we should validate the relationships sometime.
-has 'equivalence' => (
- is => 'ro',
- isa => 'HashRef[Text::Tradition::Collation::Reading]',
- default => {},
- );
+ is => 'rw',
+ isa => 'ArrayRef[Text::Tradition::Collation::Reading]',
+ );
+
+# Deal with the non-arg option for Graph::Easy's constructor.
+around BUILDARGS => sub {
+ my $orig = shift;
+ my $class = shift;
+
+ my %args;
+ if( @_ == 1 && ref( $_[0] ) ne 'HASH' ) {
+ return $class->$orig( 'name' => $_[0] );
+ } else {
+ return $class->$orig( @_ );
+ }
+};
+
+# Take constructor args as well as a Position argument.
+around position => sub {
+ my $orig = shift;
+ my $self = shift;
+ return $self->$orig() unless @_;
+
+ my @args = @_;
+ unless( @_ == 1 && ref( $_[0] ) eq 'Text::Tradition::Collation::Position' ) {
+ # We have constructor arguments; pass them to Position.
+ @args = ( Text::Tradition::Collation::Position->new( @_ ) );
+ }
+ $self->$orig( @args );
+};
+
+# Initialize the identity pool.
+sub BUILD {
+ my( $self, $args ) = @_;
+ $self->same_as( [ $self ] );
+}
+
+sub text {
+ # Wrapper function around 'label' attribute.
+ my $self = shift;
+ if( @_ ) {
+ if( defined $_[0] ) {
+ $self->set_attribute( 'label', $_[0] );
+ } else {
+ $self->del_attribute( 'label' );
+ }
+ }
+ return $self->label;
+}
+
+sub witnessed_by {
+ my( $self, $sigil, $backup ) = @_;
+ my @wits = $self->witnesses;
+ return 1 if grep { $_ eq $sigil } @wits;
+ if( $backup ) {
+ return 1 if grep { $_ eq $backup } @wits;
+ }
+ return 0;
+}
+
+sub witnesses {
+ my( $self ) = @_;
+ my @paths = grep { $_->get_attribute( 'class' ) eq 'path' } $self->outgoing;
+ push( @paths, grep { $_->get_attribute( 'class' ) eq 'path' } $self->incoming );
+ my %wits;
+ foreach my $p ( @paths ) {
+ if( $p->has_hidden_witnesses ) {
+ foreach ( @{$p->hidden_witnesses} ) {
+ $wits{$_} = 1;
+ }
+ } else {
+ $wits{$p->label} = 1;
+ }
+ }
+ return keys %wits;
+}
sub merge_from {
my( $self, $merged_node ) = @_;
my $new_pool = _merge_array_pool( \@now_identical, $self->same_as )
if @now_identical;
- # Adopt the equivalence attributes of the other node.
- my $now_equiv = $merged_node->equivalence;
- foreach my $key ( %$now_equiv ) {
- if( $self->has_relationship( $key ) ) {
- my $related = $self->get_relationship( $key );
- if( $now_equiv->{$key} ne $related ) {
- warn( sprintf( "Merged reading %s has relationship %s to reading %s instead of %s; skipping",
- $merged_node->name, $key,
- $now_equiv->{$key},
- $related) );
- } # else no action needed
- } else {
- $self->set_relationship( $key, $now_equiv->{$key} );
- }
- }
+ # TODO Adopt the relationship attributes and segment memberships of the other node.
}
+## Dealing with transposed readings. These methods are only really
+## applicable if we have a linear collation graph.
+
sub set_identical {
my( $self, $other_node ) = @_;
my $enlarged_pool = _merge_array_pool( $self->same_as,
$other_node->same_as );
# ...and set this node to point to the enlarged pool.
- $self->set_same_as( $enlarged_pool );
+ $self->same_as( $enlarged_pool );
}
+sub identical_readings {
+ my $self = shift;
+ my @same = grep { $_ ne $self } @{$self->same_as};
+ return @same;
+}
+
sub _merge_array_pool {
my( $pool, $main_pool ) = @_;
my %poolhash;
}
return $main_pool;
}
+
+sub has_primary {
+ my $self = shift;
+ my $pool = $self->same_as;
+ return $pool->[0]->name ne $self->name;
+}
+
+sub primary {
+ my $self = shift;
+ return $self->same_as->[0];
+}
+
+sub is_at_position {
+ my $self = shift;
+ return undef unless $self->has_position;
+ return $self->position->is_colocated( @_ );
+}
+
+# Returns all readings that adjoin this one on any path.
+sub neighbor_readings {
+ my( $self, $direction ) = @_;
+ $direction = 'both' unless $direction;
+ my @paths = grep { $_->isa( 'Text::Tradition::Collation::Path' ) } $self->edges;
+ my %connected;
+ foreach my $p ( @paths ) {
+ if( $p->to eq $self ) {
+ next if $direction eq 'forward';
+ $connected{$p->from->name} = $p->from;
+ } else { # $p->from eq $self
+ next if $direction =~ /^back/;
+ $connected{$p->to->name} = $p->to;
+ }
+ }
+ return values( %connected );
+}
+
+# Returns all readings related to the one we've got.
+sub related_readings {
+ my( $self, $colocated ) = @_;
+ my @related;
+ foreach my $e ( $self->edges ) {
+ next unless $e->isa( 'Text::Tradition::Collation::Relationship' );
+ next if $colocated && $e->type eq 'repetition';
+ push( @related, $e->from eq $self ? $e->to : $e->from );
+ }
+ return @related;
+}
+
+## Keep track of which readings are unchanged across witnesses.
+sub is_common {
+ my( $self ) = shift;
+ return $self->get_attribute( 'class' ) eq 'common';
+}
+
+sub make_common {
+ my( $self ) = shift;
+ $self->set_attribute( 'class', 'common' );
+}
+
+sub make_variant {
+ my( $self ) = shift;
+ $self->set_attribute( 'class', 'variant' );
+}
+
+no Moose;
+__PACKAGE__->meta->make_immutable;
+
+1;
+
+######################################################
+## copied from Graph::Easy::Parser docs
+######################################################
+# when overriding nodes, we also need ::Anon
+
+package Text::Tradition::Collation::Reading::Anon;
+use Moose;
+use MooseX::NonMoose;
+extends 'Text::Tradition::Collation::Reading';
+extends 'Graph::Easy::Node::Anon';
+no Moose;
+__PACKAGE__->meta->make_immutable;
+
+1;
+# use base qw/Text::Tradition::Collation::Reading/;
+# use base qw/Graph::Easy::Node::Anon/;
+
+######################################################
+# and :::Empty
+
+package Text::Tradition::Collation::Reading::Empty;
+use Moose;
+use MooseX::NonMoose;
+extends 'Graph::Easy::Node::Empty';
+no Moose;
+__PACKAGE__->meta->make_immutable;
+
+1;
+# use base qw/Text::Tradition::Collation::Reading/;
+
+######################################################