File Coverage

blib/lib/Treex/PML/Schema/Derive.pm
Criterion Covered Total %
statement 109 136 80.1
branch 39 62 62.9
condition 18 33 54.5
subroutine 10 12 83.3
pod 3 4 75.0
total 179 247 72.4


line stmt bran cond sub pod time code
1             package Treex::PML::Schema::Derive;
2              
3 8     8   50 use strict;
  8         17  
  8         264  
4 8     8   33 use warnings;
  8         13  
  8         425  
5              
6 8     8   46 use vars qw($VERSION);
  8         16  
  8         459  
7             BEGIN {
8 8     8   205 $VERSION='2.28'; # version template
9             }
10 8     8   40 no warnings 'uninitialized';
  8         15  
  8         313  
11 8     8   44 use Carp;
  8         14  
  8         517  
12 8     8   43 use Treex::PML::Schema::Constants;
  8         14  
  8         1034  
13 8     8   52 use base qw(Treex::PML::Schema::XMLNode);
  8         13  
  8         13086  
14              
15 0     0 1 0 sub get_decl_type { return(PML_DERIVE_DECL); }
16 0     0 1 0 sub get_decl_type_str { return('derive'); }
17              
18             sub init {
19 18     18 0 44 my ($derive,$opts)=@_;
20 18 50       66 if (!exists($derive->{type})) {
21 0         0 die " must have a type attribute\n";
22             }
23 18 100       56 if (!exists($derive->{name})) {
24 16         53 $derive->{name}=$derive->{type};
25             }
26             }
27              
28             sub simplify {
29 18     18 1 87 my ($derive,$opts)=@_;
30 18   33     135 $derive->{name} ||= $derive->{-name};
31 18         38 my $schema = $derive->{-parent};
32             return if
33             (($schema->get_decl_type == PML_TEMPLATE_DECL and $opts->{no_template_derive}) or
34 18 50 66     68 ($schema->get_decl_type == PML_SCHEMA_DECL and $opts->{no_derive}));
      66        
      33        
35              
36 18         45 my $name = $derive->{name};
37 18         33 my $type;
38 18         66 my $source = $derive->{type};
39 18 50 33     92 unless (defined($source) and length($source)) {
40 0         0 croak "Derive must specify source type in the attribute 'type' in $schema->{URL}\n";
41             }
42 18 50 33     81 if (defined($name) and length($name)) {
43 18 50 66     99 if (exists ($schema->{type}{$name}) and $source ne $name) {
44 0         0 croak "Refusing to derive already existing type '$name' from '$source' in $schema->{URL}\n";
45             }
46 18         72 $type = $schema->{type}{$name} = $schema->copy_decl($schema->{type}{$source});
47 18         55 $type->{-name} = $name;
48             } else {
49 0         0 $name = $source;
50 0         0 $type = $schema->{type}{$name};
51             }
52              
53             # deriving possible for structures, sequences and choices
54 18 100       97 if ($derive->{structure}) {
    100          
    100          
    50          
55 7 50       30 if ($type->{structure}) {
56 7         21 my $derive_structure = $derive->{structure};
57 7         16 my $target_structure = $type->{structure};
58 7         17 foreach my $attr (qw(role name)) {
59 14 100       43 if (exists $derive_structure->{$attr}) {
60 3         8 $target_structure->{$attr} = $derive_structure->{$attr};
61 0         0 push @{$target_structure->{-attributes}},$attr
62 3 50       9 unless grep { $_ eq $attr } @{$target_structure->{-attributes}};
  3         19  
  3         35  
63             }
64             }
65 7   50     27 $target_structure->{member} ||= {};
66 7         14 my $members = $target_structure->{member};
67 7         15 while (my ($member,$value) = each %{$derive_structure->{member}}) {
  16         93  
68 9         46 $members->{$member} = $target_structure->copy_decl($value); # FIXME: no need if we remove derives in the end
69             }
70 7 50       49 if (ref $derive_structure->{delete}) {
71 0         0 for my $member (@{$derive_structure->{delete}}) {
  0         0  
72 0         0 delete $members->{$member};
73             }
74             }
75             } else {
76              
77 0         0 croak "Cannot derive structure type '$name' from a non-structure '$source'\n";
78             }
79             } elsif ($derive->{sequence}) {
80 2 50       10 if ($type->{sequence}) {
81 2         6 my $derive_sequence = $derive->{sequence};
82 2         6 my $target_sequence = $type->{sequence};
83 2 50       8 if (exists $derive_sequence->{role}) {
84 0         0 $target_sequence->{role} = $derive_sequence->{role};
85 0         0 push @{$target_sequence->{-attributes}},'role'
86 0 0       0 unless grep { $_ eq 'role' } @{$target_sequence->{-attributes}};
  0         0  
  0         0  
87             }
88 2 50       9 if (exists $derive_sequence->{content_pattern}) {
89 2         9 $target_sequence->{content_pattern} = $derive_sequence->{content_pattern};
90 2         21 push @{$target_sequence->{-attributes}},'content_pattern'
91 2 50       5 unless grep { $_ eq 'content_pattern' } @{$target_sequence->{-attributes}};
  2         11  
  2         7  
92             }
93 2   50     12 $target_sequence->{element} ||= {};
94 2         6 my $elements = $target_sequence->{element};
95 2         19 while (my ($element,$value) = each %{$derive_sequence->{element}}) {
  4         28  
96 2         19 $elements->{$element} = $target_sequence->copy_decl($value); # FIXME: no need if we remove derives in the end
97             }
98 2 50       33 if (ref $derive_sequence->{delete}) {
99 0         0 for my $element (@{$derive_sequence->{delete}}) {
  0         0  
100 0         0 delete $elements->{$element};
101             }
102             }
103             } else {
104 0         0 require Data::Dumper;
105             # print STDERR Data::Dumper::Dumper([$type]);
106 0         0 croak "Cannot derive sequence type '$name' from a non-sequence '$source'\n";
107             }
108             } elsif ($derive->{container}) {
109 7 50       26 if ($type->{container}) {
110 7         18 my $derive_container = $derive->{container};
111 7         15 my $target_container = $type->{container};
112 7         18 for my $attr (qw(type role)) {
113 14 100       49 next unless exists $derive_container->{$attr};
114 2 100 66     7 if ($attr eq 'type' and !exists($target_container->{type})) {
115 1         2 foreach my $d (qw(list alt structure container sequence cdata)) {
116 6 100       10 if (exists $target_container->{$d}) {
117 1         3 delete $target_container->{$d};
118 1         2 last;
119             }
120             }
121 1         2 delete $target_container->{-decl};
122 1         3 delete $target_container->{-resolved};
123             }
124 2         5 $target_container->{$attr} = $derive_container->{$attr};
125 1         3 push @{$target_container->{-attributes}},$attr
126 2 100       2 unless grep { $_ eq $attr } @{$target_container->{-attributes}};
  3         7  
  2         4  
127             }
128 7   100     52 $target_container->{attribute} ||= {};
129 7         17 my $attributes = $target_container->{attribute};
130 7         17 while (my ($attribute,$value) = each %{$derive_container->{attribute}}) {
  14         83  
131 7         34 $attributes->{$attribute} = $target_container->copy_decl($value); # FIXME: no need if we remove derives in the end
132             }
133 7 100       43 if (ref $derive_container->{delete}) {
134 1         3 for my $attribute (@{$derive_container->{delete}}) {
  1         4  
135 1         6 delete $attributes->{$attribute};
136             }
137             }
138             } else {
139 0         0 croak "Cannot derive a container '$name' from a different type '$source'\n";
140             }
141             } elsif ($derive->{choice}) {
142 2         6 my $choice = $derive->{choice};
143 2 50       9 if ($type->{choice}) {
144 2         6 my (@add,%delete);
145 2 50       9 if (UNIVERSAL::isa($choice,'HASH')) {
146 2 50       11 @add = @{$choice->{values}} if ref $choice->{values};
  2         11  
147 2 50       11 @delete{ @{$choice->{delete}} }=() if ref $choice->{delete};
  2         10  
148             } else {
149 0         0 @add = @$choice;
150             }
151 2         5 my %seen;
152 2         24 @{$type->{choice}{values}} =
153 2   66     4 grep { !($seen{$_}++) and ! exists $delete{$_} } (@{$type->{choice}{values}},@add);
  16         93  
  2         8  
154             } else {
155 0           croak "Cannot derive a choice type '$name' from a non-choice type '$source'\n";
156             }
157             } else {
158 0 0         unless ($name ne $source) {
159 0           croak " has no effect in $schema->{URL}\n";
160             }
161             }
162             }
163              
164             1;
165             __END__