145eba4c92e5ba074d25df96dc7599e110307824
[scpubgit/stemmatology.git] / lib / Text / Tradition / Witness.pm
1 package Text::Tradition::Witness;
2 use Moose;
3 use Moose::Util::TypeConstraints;
4
5 # Sigil. Required identifier for a witness.
6 has 'sigil' => (
7     is => 'ro',
8     isa => 'Str',
9     required => 1,
10     );
11
12 # Text.  This is an array of strings (i.e. word tokens).
13 # TODO Think about how to handle this for the case of pre-prepared
14 # collations, where the tokens are in the graph already.
15 has 'text' => (
16     is => 'rw',
17     isa => 'ArrayRef[Str]',
18     predicate => 'has_text',
19     );
20
21 # Source.  This is where we read in the witness, if not from a
22 # pre-prepared collation.  It is probably a filename.
23 has 'source' => (
24     is => 'ro',
25     isa => 'Str',
26     predicate => 'has_source',
27     );
28
29 has 'path' => (
30     is => 'rw',
31     isa => 'ArrayRef[Text::Tradition::Collation::Reading]',
32     predicate => 'has_path',
33     );         
34
35 subtype 'Correction',
36     as 'ArrayRef',
37     where { @{$_} == 3 &&
38             find_type_constraint('Int')->check( $_->[0] ) &&
39             find_type_constraint('Int')->check( $_->[1] ) &&
40             find_type_constraint('ArrayRef[Text::Tradition::Collation::Reading]')->check( $_->[2] );
41     },
42     message { 'Correction must be a tuple of [offset, length, list]' };
43
44 has 'ante_corr' => (
45     is => 'rw',
46     isa => 'ArrayRef[Correction]',
47     predicate => 'has_ante_corr',
48     );
49     
50
51 sub BUILD {
52     my $self = shift;
53     if( $self->has_source ) {
54         # Read the file and initialize the text.
55         open( WITNESS, $self->source ) or die "Could not open " 
56             . $self->file . "for reading";
57         # TODO support TEI as well as plaintext, sometime
58         my @words;
59         while(<WITNESS>) {
60             chomp;
61             push( @words, split( /\s+/, $_ ) );
62         }
63         close WITNESS;
64         $self->text( \@words );
65     }
66 }
67
68 # If the text is not present, and the path is, and this is a 'get'
69 # request, generate text from path.
70 around text => sub {
71     my $orig = shift;
72     my $self = shift;
73
74     if( $self->has_path && !$self->has_text && !@_ ) {
75         my @words = map { $_->label } @{$self->path};
76         $self->$orig( \@words );
77     }
78     
79     $self->$orig( @_ );
80 };
81
82 sub uncorrected_path {
83     my $self = shift;
84
85     my @new_path;
86     push( @new_path, @{$self->path} );
87     my $drift = 0;
88     foreach my $change ( @{$self->ante_corr} ) {
89         my( $offset, $length, $items ) = @$change;
90         my $realoffset = $offset + $drift;
91         splice( @new_path, $realoffset, $length, @$items );
92         $drift += @$items - $length;
93     }
94     return \@new_path;
95 }
96     
97
98 no Moose;
99 __PACKAGE__->meta->make_immutable;