tweak options for classifying readings and relationships. Needed for tla/stemmaweb#33
[scpubgit/stemmatology.git] / base / lib / Text / Tradition / Collation / Relationship.pm
1 package Text::Tradition::Collation::Relationship;
2
3 use Moose;
4 use Moose::Util::TypeConstraints;
5
6 enum 'RelationshipScope' => [ qw( local document global ) ];
7
8 no Moose::Util::TypeConstraints;
9
10 =head1 NAME
11
12 Text::Tradition::Collation::Relationship - represents a syntactic or semantic
13 relationship between two readings
14     
15 =head1 DESCRIPTION
16
17 Text::Tradition is a library for representation and analysis of collated
18 texts, particularly medieval ones.  A relationship connects two readings
19 within a collation, usually when they appear in the same place in different
20 texts.
21
22 =head1 CONSTRUCTOR
23
24 =head2 new
25
26 Creates a new relationship. Usually called via $collation->add_relationship.
27 Options include:
28
29 =over 4
30
31 =item * type - Can be one of spelling, orthographic, grammatical, lexical, 
32 collated, repetition, transposition.  All but the last two are only valid 
33 relationships between readings that occur at the same point in the text. 
34 The 'collated' relationship should only be used by parsers to align readings 
35 in the graph when the input information would otherwise be lost, e.g. from
36 an alignment table.
37
38 =item * displayform - (Optional) The reading that should be displayed if the 
39 related nodes are treated as one.
40
41 =item * scope - (Optional) A meta-attribute.  Can be one of 'local', 
42 'document', or 'global'. Denotes whether the relationship between the two 
43 readings holds always, independent of context, either within this tradition 
44 or across all traditions.
45
46 =item * annotation - (Optional) A freeform note to attach to the relationship.
47
48 =item * alters_meaning - Indicate whether, in context, the related words cause
49 the text to have different meanings. Possible values are 0 (no), 1 (slightly),
50 and >1 (yes).
51
52 =item * non_correctable - (Optional) True if the reading would not have been 
53 corrected independently.
54
55 =item * non_independent - (Optional) True if the variant is unlikely to have 
56 occurred independently in unrelated witnesses.
57
58 =back
59
60 =head1 ACCESSORS
61
62 =head2 type
63
64 =head2 displayform
65
66 =head2 scope
67
68 =head2 annotation
69
70 =head2 non_correctable
71
72 =head2 non_independent
73
74 See the option descriptions above.
75
76 =cut
77
78 has 'type' => (
79         is => 'ro',
80         isa => 'Str',
81         required => 1,
82         );
83
84 has 'reading_a' => (
85         is => 'ro',
86         isa => 'Str',
87         required => 1,
88         );
89
90 has 'reading_b' => (
91         is => 'ro',
92         isa => 'Str',
93         required => 1,
94         );
95
96 has 'displayform' => (
97         is => 'ro',
98         isa => 'Str',
99         predicate => 'has_displayform',
100         );
101
102 has 'scope' => (
103         is => 'ro',
104         isa => 'RelationshipScope', 
105         default => 'local',
106         );
107         
108 has 'annotation' => (
109         is => 'ro',
110         isa => 'Str',
111         predicate => 'has_annotation',
112         );
113         
114 has 'alters_meaning' => (
115         is => 'rw',
116         isa => 'Int',
117         default => 0,
118         );
119
120 has 'a_derivable_from_b' => (
121         is => 'ro',
122         isa => 'Bool',
123         );
124         
125 has 'b_derivable_from_a' => (
126         is => 'ro',
127         isa => 'Bool',
128         );
129         
130 has 'non_independent' => (
131         is => 'ro',
132         isa => 'Bool',
133         );
134         
135 around 'alters_meaning' => sub {
136         my $orig = shift;
137         my $self = shift;
138         if( @_ ) {
139                 if( $_[0] eq 'no' ) {
140                         return $self->$orig( 0 );
141                 } elsif( $_[0] eq 'slightly' ) {
142                         return $self->$orig( 1 );
143                 } elsif( $_[0] eq 'yes' ) {
144                         return $self->$orig( 2 );
145                 } 
146         }
147         return $self->$orig( @_ );
148 };              
149         
150 # A read-only meta-Boolean attribute.
151
152 =head2 colocated
153
154 Returns true if the relationship type is one that requires that its readings
155 occupy the same place in the collation.
156
157 =cut
158
159 sub colocated {
160         my $self = shift;
161         return $self->type !~ /^(repetition|transposition)$/;
162 }
163
164 =head2 nonlocal
165
166 Returns true if the relationship scope is anything other than 'local'.
167
168 =cut
169
170 sub nonlocal {
171         my $self = shift;
172         return $self->scope ne 'local';
173 }
174
175 =head2 is_equivalent( $otherrel )
176
177 Returns true if the type and scope of $otherrel match ours.
178
179 =cut
180
181 sub is_equivalent {
182         my( $self, $other, $check_ann ) = @_;
183         my $oksofar = $self->type eq $other->type && $self->scope eq $other->scope;
184         if( $check_ann ) {
185                 return $oksofar && $self->annotation eq $other->annotation;
186         } else {
187                 return $oksofar;
188         }
189 }
190
191 no Moose;
192 __PACKAGE__->meta->make_immutable;
193
194 1;