split out XML parsing logic from CollateX logic in parser
[scpubgit/stemmatology.git] / lib / Text / Tradition.pm
1 package Text::Tradition;
2
3 use Module::Load;
4 use Moose;
5 use Text::Tradition::Collation;
6 use Text::Tradition::Witness;
7
8 has 'collation' => (
9     is => 'ro',
10     isa => 'Text::Tradition::Collation',
11     writer => '_save_collation',
12     );
13
14 has 'witnesses' => (
15     traits => ['Array'],
16     is => 'rw',
17     isa => 'ArrayRef[Text::Tradition::Witness]',
18     handles => {
19         all    => 'elements',
20         add    => 'push',
21     },
22     default => sub { [] },
23     );
24
25 has 'name' => (
26     is => 'rw',
27     isa => 'Str',
28     default => 'Tradition',
29     );
30
31 sub BUILD {
32     my( $self, $init_args ) = @_;
33
34     if( exists $init_args->{'witnesses'} ) {
35         # We got passed an uncollated list of witnesses.  Make a
36         # witness object for each witness, and then send them to the
37         # collator.
38         my $autosigil = 0;
39         foreach my $wit ( %{$init_args->{'witnesses'}} ) {
40             # Each item in the list is either a string or an arrayref.
41             # If it's a string, it is a filename; if it's an arrayref,
42             # it is a tuple of 'sigil, file'.  Handle either case.
43             my $args;
44             if( ref( $wit ) eq 'ARRAY' ) {
45                 $args = { 'sigil' => $wit->[0],
46                           'file' => $wit->[1] };
47             } else {
48                 $args = { 'sigil' => chr( $autosigil+65 ),
49                           'file' => $wit };
50                 $autosigil++;
51             }
52             $self->witnesses->push( Text::Tradition::Witness->new( $args ) );
53             # TODO Now how to collate these?
54         }
55     } else {
56         # Else we need to parse some collation data.  Make a Collation object
57         my $collation = Text::Tradition::Collation->new( %$init_args,
58                                                         'tradition' => $self );
59         $self->_save_collation( $collation );
60
61         # Call the appropriate parser on the given data
62         my @formats = grep { /^(Self|CollateX|CSV|CTE|TEI)$/ } keys( %$init_args );
63         my $format = shift( @formats );
64         unless( $format ) {
65             warn "No data given to create a collation; will initialize an empty one";
66         }
67         if( $format && $format =~ /^(CSV|CTE)$/ && 
68             !exists $init_args->{'base'} ) {
69             warn "Cannot make a collation from $format without a base text";
70             return;
71         }
72
73         # Starting point for all texts
74         my $last_node = $collation->add_reading( '#START#' );
75
76         # Now do the parsing. 
77         my @sigla;
78         if( $format ) {
79             my @parseargs;
80             if( $format =~ /^(CSV|CTE)$/ ) {
81                 $init_args->{'data'} = $init_args->{$format};
82                 $init_args->{'format'} = $format;
83                 $format = 'BaseText';
84                 @parseargs = %$init_args;
85             } else {
86                 @parseargs = ( $init_args->{ $format } ); 
87             }
88             my $mod = "Text::Tradition::Parser::$format";
89             load( $mod );
90             $mod->can('parse')->( $self, @parseargs );
91         }
92     }
93 }
94
95 sub witness {
96     my( $self, $sigil ) = @_;
97     my $requested_wit;
98     foreach my $wit ( @{$self->witnesses} ) {
99         $requested_wit = $wit if $wit->sigil eq $sigil;
100     }
101     # We depend on an undef return value for no such witness.
102     # warn "No such witness $sigil" unless $requested_wit;
103     return $requested_wit;
104 }
105         
106
107 sub add_witness {
108     my $self = shift;
109     my $new_wit = Text::Tradition::Witness->new( @_ );
110     push( @{$self->witnesses}, $new_wit );
111     return $new_wit;
112 }
113
114 # The user will usually be instantiating a Tradition object, and
115 # examining its collation.  The information about the tradition can
116 # come via several routes:
117 # - graphML from CollateX or elsewhere, standalone
118 # - TEI parallel segmentation
119 # - Leuven-style spreadsheet of variants, converted to CSV, plus base text
120 # - apparatus pulled from CTE, plus base text
121 # From this we should be able to get basic witness information.
122
123 # Alternatively the user can just give us the uncollated texts.  Then
124 # instead of passing a collation, s/he is passing a set of witnesses
125 # from which we will generate a collation.  Those witnesses can be in
126 # plaintext or in TEI with certain constraints adopted.
127
128 # So the constructor for a tradition needs to take one of these infosets,
129 # and construct the collation and the witness objects.
130
131 no Moose;
132 __PACKAGE__->meta->make_immutable;