| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package PRANG::Graph::Meta::Class; | 
| 2 |  |  |  |  |  |  | $PRANG::Graph::Meta::Class::VERSION = '0.21'; | 
| 3 | 11 |  |  | 11 |  | 38765 | use 5.010; | 
|  | 11 |  |  |  |  | 49 |  | 
| 4 | 11 |  |  | 11 |  | 85 | use Moose::Role; | 
|  | 11 |  |  |  |  | 29 |  | 
|  | 11 |  |  |  |  | 96 |  | 
| 5 | 11 |  |  | 11 |  | 59738 | use Moose::Util::TypeConstraints; | 
|  | 11 |  |  |  |  | 36 |  | 
|  | 11 |  |  |  |  | 95 |  | 
| 6 | 11 |  |  | 11 |  | 24022 | use MooseX::Params::Validate; | 
|  | 11 |  |  |  |  | 31 |  | 
|  | 11 |  |  |  |  | 102 |  | 
| 7 | 11 |  |  | 11 |  | 5853 | use XML::LibXML; | 
|  | 11 |  |  |  |  | 48376 |  | 
|  | 11 |  |  |  |  | 100 |  | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | has 'xml_attr' => | 
| 10 |  |  |  |  |  |  | isa => "HashRef[HashRef[PRANG::Graph::Meta::Attr]]", | 
| 11 |  |  |  |  |  |  | is => "ro", | 
| 12 |  |  |  |  |  |  | lazy => 1, | 
| 13 |  |  |  |  |  |  | required => 1, | 
| 14 |  |  |  |  |  |  | default => sub { | 
| 15 |  |  |  |  |  |  | my $self = shift; | 
| 16 |  |  |  |  |  |  | my @attr = grep { $_->does("PRANG::Graph::Meta::Attr") } | 
| 17 |  |  |  |  |  |  | $self->get_all_attributes; | 
| 18 |  |  |  |  |  |  | my $default_xmlns = ""; #eval { $self->name->xmlns }; | 
| 19 |  |  |  |  |  |  | my %attr_ns; | 
| 20 |  |  |  |  |  |  | for my $attr (@attr) { | 
| 21 |  |  |  |  |  |  | my $xmlns = $attr->has_xmlns | 
| 22 |  |  |  |  |  |  | ? | 
| 23 |  |  |  |  |  |  | $attr->xmlns | 
| 24 |  |  |  |  |  |  | : $default_xmlns; | 
| 25 |  |  |  |  |  |  | my $xml_name = $attr->has_xml_name | 
| 26 |  |  |  |  |  |  | ? | 
| 27 |  |  |  |  |  |  | $attr->xml_name | 
| 28 |  |  |  |  |  |  | : $attr->name; | 
| 29 |  |  |  |  |  |  | $attr_ns{$xmlns//""}{$xml_name} = $attr; | 
| 30 |  |  |  |  |  |  | } | 
| 31 |  |  |  |  |  |  | \%attr_ns; | 
| 32 |  |  |  |  |  |  | }; | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | has 'xml_elements' => | 
| 35 |  |  |  |  |  |  | isa => "ArrayRef[PRANG::Graph::Meta::Element]", | 
| 36 |  |  |  |  |  |  | is => "ro", | 
| 37 |  |  |  |  |  |  | lazy => 1, | 
| 38 |  |  |  |  |  |  | required => 1, | 
| 39 |  |  |  |  |  |  | default => sub { | 
| 40 |  |  |  |  |  |  | my $self = shift; | 
| 41 |  |  |  |  |  |  | my @elements = grep { | 
| 42 |  |  |  |  |  |  | $_->does("PRANG::Graph::Meta::Element") | 
| 43 |  |  |  |  |  |  | } $self->get_all_attributes; | 
| 44 |  |  |  |  |  |  | my @e_c = map { $_->associated_class->name } @elements; | 
| 45 |  |  |  |  |  |  | my %e_c_does; | 
| 46 |  |  |  |  |  |  | for my $parent (@e_c) { | 
| 47 |  |  |  |  |  |  | for my $child (@e_c) { | 
| 48 |  |  |  |  |  |  | if ( $parent eq $child ) { | 
| 49 |  |  |  |  |  |  | $e_c_does{$parent}{$child} = 0; | 
| 50 |  |  |  |  |  |  | } | 
| 51 |  |  |  |  |  |  | else { | 
| 52 |  |  |  |  |  |  | my $cdp = $child->does($parent) || | 
| 53 |  |  |  |  |  |  | $child->isa($parent); | 
| 54 |  |  |  |  |  |  | $e_c_does{$parent}{$child} = | 
| 55 |  |  |  |  |  |  | ( $cdp ? -1 : 1 ); | 
| 56 |  |  |  |  |  |  | } | 
| 57 |  |  |  |  |  |  | } | 
| 58 |  |  |  |  |  |  | } | 
| 59 |  |  |  |  |  |  | [   map { $elements[$_] } sort { | 
| 60 |  |  |  |  |  |  | $e_c_does{$e_c[$a]}{$e_c[$b]} or | 
| 61 |  |  |  |  |  |  | ( | 
| 62 |  |  |  |  |  |  | $elements[$a]->insertion_order | 
| 63 |  |  |  |  |  |  | <=> $elements[$b]->insertion_order | 
| 64 |  |  |  |  |  |  | ) | 
| 65 |  |  |  |  |  |  | } 0..$#elements | 
| 66 |  |  |  |  |  |  | ]; | 
| 67 |  |  |  |  |  |  | }; | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | has 'graph' => | 
| 70 |  |  |  |  |  |  | is => "rw", | 
| 71 |  |  |  |  |  |  | isa => "PRANG::Graph::Node", | 
| 72 |  |  |  |  |  |  | lazy => 1, | 
| 73 |  |  |  |  |  |  | required => 1, | 
| 74 |  |  |  |  |  |  | default => sub { | 
| 75 |  |  |  |  |  |  | $_[0]->build_graph; | 
| 76 |  |  |  |  |  |  | }, | 
| 77 |  |  |  |  |  |  | ; | 
| 78 |  |  |  |  |  |  |  | 
| 79 |  |  |  |  |  |  | sub build_graph { | 
| 80 | 41 |  |  | 41 | 0 | 88 | my $self = shift; | 
| 81 |  |  |  |  |  |  |  | 
| 82 | 41 |  |  |  |  | 82 | my @nodes = map { $_->graph_node } @{ $self->xml_elements }; | 
|  | 92 |  |  |  |  | 2773 |  | 
|  | 41 |  |  |  |  | 1271 |  | 
| 83 | 41 | 100 |  |  |  | 277 | if ( @nodes != 1 ) { | 
|  |  | 50 |  |  |  |  |  | 
| 84 | 12 |  |  |  |  | 96 | PRANG::Graph::Seq->new( | 
| 85 |  |  |  |  |  |  | members => \@nodes, | 
| 86 |  |  |  |  |  |  | ); | 
| 87 |  |  |  |  |  |  | } | 
| 88 |  |  |  |  |  |  | elsif (@nodes) { | 
| 89 | 29 |  |  |  |  | 892 | $nodes[0]; | 
| 90 |  |  |  |  |  |  | } | 
| 91 |  |  |  |  |  |  | } | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  | sub add_to_list($$) { | 
| 94 | 1360 | 100 |  | 1360 | 0 | 2815 | if ( !defined $_[0] ) { | 
| 95 | 1052 |  |  |  |  | 3587 | $_[0] = $_[1]; | 
| 96 |  |  |  |  |  |  | } | 
| 97 |  |  |  |  |  |  | else { | 
| 98 | 308 | 100 | 100 |  |  | 1054 | if ( (ref($_[0])||"") ne "ARRAY" ) { | 
| 99 | 153 |  |  |  |  | 321 | $_[0] = [ $_[0] ]; | 
| 100 |  |  |  |  |  |  | } | 
| 101 | 308 |  |  |  |  | 408 | push @{ $_[0] }, $_[1]; | 
|  | 308 |  |  |  |  | 917 |  | 
| 102 |  |  |  |  |  |  | } | 
| 103 |  |  |  |  |  |  | } | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | sub as_listref($) { | 
| 106 | 107 | 100 |  | 107 | 0 | 337 | if ( ref($_[0]) eq "ARRAY" ) { | 
| 107 | 51 |  |  |  |  | 454 | $_[0] | 
| 108 |  |  |  |  |  |  | } | 
| 109 |  |  |  |  |  |  | else { | 
| 110 | 56 |  |  |  |  | 524 | [ $_[0] ]; | 
| 111 |  |  |  |  |  |  | } | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | sub as_item($) { | 
| 115 | 211 | 50 |  | 211 | 0 | 562 | if ( ref($_[0]) eq "ARRAY" ) { | 
| 116 | 0 |  |  |  |  | 0 | die scalar(@{$_[0]})." item(s) found where 1 expected"; | 
|  | 0 |  |  |  |  | 0 |  | 
| 117 |  |  |  |  |  |  | } | 
| 118 |  |  |  |  |  |  | else { | 
| 119 | 211 |  |  |  |  | 618 | $_[0]; | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  | } | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | sub accept_attributes { | 
| 124 | 264 |  |  | 264 | 1 | 53298 | my $self = shift; | 
| 125 | 264 |  |  |  |  | 1585 | my ( $node_attr, $context, $lax ) = pos_validated_list( | 
| 126 |  |  |  |  |  |  | \@_, | 
| 127 |  |  |  |  |  |  | { isa => 'ArrayRef[XML::LibXML::Attr]' }, | 
| 128 |  |  |  |  |  |  | { isa => 'PRANG::Graph::Context' }, | 
| 129 |  |  |  |  |  |  | { isa => 'Bool', optional => 1, default => 0 }, | 
| 130 |  |  |  |  |  |  | ); | 
| 131 |  |  |  |  |  |  |  | 
| 132 | 264 |  |  |  |  | 127876 | my $attributes = $self->xml_attr; | 
| 133 | 264 |  |  |  |  | 541 | my %rv; | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | # process attributes | 
| 136 | 264 |  |  |  |  | 646 | for my $attr (@$node_attr) { | 
| 137 | 100 |  |  |  |  | 447 | my $prefix = $attr->prefix; | 
| 138 | 100 | 100 |  |  |  | 256 | if ( !defined $prefix ) { | 
| 139 | 74 |  |  |  |  | 127 | $prefix = ""; | 
| 140 |  |  |  |  |  |  | } | 
| 141 | 100 | 50 | 66 |  |  | 970 | if ( length $prefix and !exists $context->xsi->{$prefix} ) { | 
| 142 | 0 |  |  |  |  | 0 | $context->exception("unknown xmlns prefix '$prefix'"); | 
| 143 |  |  |  |  |  |  | } | 
| 144 | 100 | 100 |  |  |  | 330 | my $xmlns = $context->get_xmlns($prefix) | 
| 145 |  |  |  |  |  |  | if length $prefix; | 
| 146 | 100 |  | 100 |  |  | 304 | $xmlns //= ""; | 
| 147 | 100 |  |  |  |  | 488 | my $meta_att = $attributes->{$xmlns}{$attr->localname}; | 
| 148 | 100 |  |  |  |  | 164 | my $xmlns_att_name; | 
| 149 |  |  |  |  |  |  | my $_xmlns_att_name = sub { | 
| 150 | 34 | 50 |  | 34 |  | 1043 | $xmlns_att_name = $meta_att->xmlns_attr | 
| 151 |  |  |  |  |  |  | or $context->exception( | 
| 152 |  |  |  |  |  |  | "xmlns wildcarded, but no xmlns_attr set on " | 
| 153 |  |  |  |  |  |  | .$self->name." property '" | 
| 154 |  |  |  |  |  |  | .$meta_att->att_name."'", | 
| 155 |  |  |  |  |  |  | ); | 
| 156 | 100 |  |  |  |  | 460 | }; | 
| 157 |  |  |  |  |  |  |  | 
| 158 | 100 | 100 | 33 |  |  | 505 | if ($meta_att) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 159 |  |  |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | # sweet, it's ok | 
| 161 | 57 |  |  |  |  | 188 | my $att_name = $meta_att->name; | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | # check the type constraint | 
| 164 | 57 | 100 | 66 |  |  | 2065 | if ( my $tc = $meta_att->type_constraint | 
| 165 |  |  |  |  |  |  | and !$meta_att->xml_isa ) | 
| 166 |  |  |  |  |  |  | { | 
| 167 | 56 | 100 |  |  |  | 349 | if ( !$tc->check($attr->value) ) { | 
| 168 | 2 |  |  |  |  | 183 | $context->exception( | 
| 169 |  |  |  |  |  |  | "invalid value '" . $attr->value . "' of attribute ".$attr->nodeName, | 
| 170 |  |  |  |  |  |  | $attr->parentNode, | 
| 171 |  |  |  |  |  |  | ); | 
| 172 |  |  |  |  |  |  | } | 
| 173 |  |  |  |  |  |  | } | 
| 174 | 55 |  |  |  |  | 2432 | add_to_list($rv{$att_name}, $attr->value); | 
| 175 |  |  |  |  |  |  | } | 
| 176 |  |  |  |  |  |  | elsif ( $meta_att = $attributes->{"*"}{$attr->localname} ) { | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | # wildcard xmlns only; need to store the xmlns | 
| 179 |  |  |  |  |  |  | # in another attribute.  Also, multiple values | 
| 180 |  |  |  |  |  |  | # may appear with different xml namespaces. | 
| 181 | 6 |  |  |  |  | 20 | my $att_name = $meta_att->name; | 
| 182 | 6 |  |  |  |  | 17 | $_xmlns_att_name->(); | 
| 183 | 6 |  |  |  |  | 41 | add_to_list($rv{$att_name}, $attr->value); | 
| 184 | 6 |  |  |  |  | 22 | add_to_list($rv{$xmlns_att_name}, $xmlns); | 
| 185 |  |  |  |  |  |  | } | 
| 186 |  |  |  |  |  |  | elsif ( $meta_att = $attributes->{$xmlns}{"*"} ) { | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | # wildcard attribute name.  This attribute gets | 
| 189 |  |  |  |  |  |  | # HashRef treatment. | 
| 190 | 6 |  |  |  |  | 69 | $rv{$meta_att->name}{$attr->localname} = $attr->value; | 
| 191 |  |  |  |  |  |  | } | 
| 192 |  |  |  |  |  |  | elsif ( $meta_att = $attributes->{"*"}{"*"} ) { | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | # wildcard attribute name and namespace.  Both | 
| 195 |  |  |  |  |  |  | # attributes gets the joy of HashRef[ArrayRef[Str]|Str] | 
| 196 | 28 |  |  |  |  | 121 | my $att_name = $meta_att->name; | 
| 197 | 28 |  |  |  |  | 65 | $_xmlns_att_name->(); | 
| 198 |  |  |  |  |  |  | add_to_list( | 
| 199 | 28 |  |  |  |  | 242 | $rv{$att_name}{$attr->localname}, | 
| 200 |  |  |  |  |  |  | $attr->value, | 
| 201 |  |  |  |  |  |  | ); | 
| 202 |  |  |  |  |  |  | add_to_list( | 
| 203 | 28 |  |  |  |  | 137 | $rv{$xmlns_att_name}{$attr->localname}, | 
| 204 |  |  |  |  |  |  | $xmlns | 
| 205 |  |  |  |  |  |  | ); | 
| 206 |  |  |  |  |  |  | } | 
| 207 |  |  |  |  |  |  | elsif ( | 
| 208 |  |  |  |  |  |  | $xmlns =~ m{^\w+://\w+\.w3\.org/.*schema}i | 
| 209 |  |  |  |  |  |  | and | 
| 210 |  |  |  |  |  |  | $attr->localname =~ m{schema}i | 
| 211 |  |  |  |  |  |  | ) | 
| 212 |  |  |  |  |  |  | { | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | # they said "schema" twice, they must be mad. | 
| 215 |  |  |  |  |  |  | # ignore their craven input. | 
| 216 |  |  |  |  |  |  | } | 
| 217 |  |  |  |  |  |  | else { | 
| 218 |  |  |  |  |  |  | # fail, unless we're in lax mode, in which case do nothing. | 
| 219 | 3 | 100 |  |  |  | 26 | $context->exception("invalid attribute '".$attr->name."'") | 
| 220 |  |  |  |  |  |  | unless $lax; | 
| 221 |  |  |  |  |  |  | } | 
| 222 |  |  |  |  |  |  | } | 
| 223 | 260 |  |  |  |  | 940 | (%rv); | 
| 224 |  |  |  |  |  |  | } | 
| 225 |  |  |  |  |  |  |  | 
| 226 | 11 |  |  | 11 |  | 23700 | use JSON; | 
|  | 11 |  |  |  |  | 113101 |  | 
|  | 11 |  |  |  |  | 130 |  | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | sub accept_childnodes { | 
| 229 | 272 |  |  | 272 | 1 | 28192 | my $self = shift; | 
| 230 |  |  |  |  |  |  |  | 
| 231 | 272 |  |  |  |  | 1798 | my ( $childNodes, $context, $lax ) = pos_validated_list( | 
| 232 |  |  |  |  |  |  | \@_, | 
| 233 |  |  |  |  |  |  | { isa => 'ArrayRef[XML::LibXML::Node]' }, | 
| 234 |  |  |  |  |  |  | { isa => 'PRANG::Graph::Context' }, | 
| 235 |  |  |  |  |  |  | { isa => 'Bool', optional => 1, default => 0 }, | 
| 236 |  |  |  |  |  |  | ); | 
| 237 |  |  |  |  |  |  |  | 
| 238 | 272 |  |  |  |  | 117891 | my $graph = $self->graph; | 
| 239 |  |  |  |  |  |  |  | 
| 240 | 272 |  |  |  |  | 823 | my (%init_args, %init_arg_names, %init_arg_xmlns, %init_arg_nodes); | 
| 241 | 272 |  |  |  |  | 0 | my @rv; | 
| 242 |  |  |  |  |  |  | my @nodes = grep { | 
| 243 | 272 |  | 100 |  |  | 1032 | !(  $_->isa("XML::LibXML::Text") | 
|  | 436 |  |  |  |  | 2503 |  | 
| 244 |  |  |  |  |  |  | and $_->data =~ /\A\s*\Z/ | 
| 245 |  |  |  |  |  |  | ) | 
| 246 |  |  |  |  |  |  | } | 
| 247 |  |  |  |  |  |  | @$childNodes; | 
| 248 | 272 |  |  |  |  | 1508 | while ( my $input_node = shift @nodes ) { | 
| 249 | 410 | 50 |  |  |  | 3373 | next if $input_node->nodeType == XML_COMMENT_NODE; | 
| 250 | 410 |  |  |  |  | 1620 | my ($key, $value, $name, $xmlns) = | 
| 251 |  |  |  |  |  |  | $graph->accept($input_node, $context, $lax); | 
| 252 | 383 | 100 |  |  |  | 927 | if ( !$key ) { | 
| 253 | 5 | 100 |  |  |  | 51 | next if $lax; | 
| 254 |  |  |  |  |  |  |  | 
| 255 | 2 |  |  |  |  | 15 | my (@what) = $graph->expected($context); | 
| 256 | 2 |  |  |  |  | 13 | $context->exception( | 
| 257 |  |  |  |  |  |  | "unexpected node: expecting @what", | 
| 258 |  |  |  |  |  |  | $input_node, | 
| 259 |  |  |  |  |  |  | ); | 
| 260 |  |  |  |  |  |  | } | 
| 261 | 378 |  |  |  |  | 1301 | my $meta_att = $self->find_attribute_by_name($key); | 
| 262 | 378 | 100 |  |  |  | 28242 | if ( !$meta_att->_item_tc->check($value) ) { | 
| 263 | 4 |  |  |  |  | 446 | $context = $context->next_ctx( | 
| 264 |  |  |  |  |  |  | $input_node->namespaceURI, | 
| 265 |  |  |  |  |  |  | $input_node->localname, | 
| 266 |  |  |  |  |  |  | ); | 
| 267 | 4 |  |  |  |  | 32 | $context->exception( | 
| 268 |  |  |  |  |  |  | "bad value '$value'", | 
| 269 |  |  |  |  |  |  | $input_node | 
| 270 |  |  |  |  |  |  | ); | 
| 271 |  |  |  |  |  |  | } | 
| 272 | 374 |  |  |  |  | 40318 | add_to_list($init_args{$key}, $value); | 
| 273 | 374 |  |  |  |  | 1362 | add_to_list($init_arg_nodes{$key}, $input_node); | 
| 274 | 374 | 100 |  |  |  | 876 | if ( defined $name ) { | 
| 275 |  |  |  |  |  |  | add_to_list( | 
| 276 | 135 |  |  |  |  | 344 | $init_arg_names{$key}, | 
| 277 |  |  |  |  |  |  | $name, | 
| 278 |  |  |  |  |  |  | ); | 
| 279 |  |  |  |  |  |  | } | 
| 280 | 374 | 100 |  |  |  | 853 | if ( defined $xmlns ) { | 
| 281 |  |  |  |  |  |  | add_to_list( | 
| 282 | 354 |  |  |  |  | 891 | $init_arg_xmlns{$key}, | 
| 283 |  |  |  |  |  |  | $xmlns, | 
| 284 |  |  |  |  |  |  | ); | 
| 285 |  |  |  |  |  |  | } | 
| 286 |  |  |  |  |  |  | } | 
| 287 |  |  |  |  |  |  |  | 
| 288 | 239 | 100 |  |  |  | 1146 | if ( !$graph->complete($context) ) { | 
| 289 | 6 |  |  |  |  | 34 | my (@what) = $graph->expected($context); | 
| 290 | 6 |  |  |  |  | 47 | $context->exception( | 
| 291 |  |  |  |  |  |  | "Node incomplete; expecting: @what", | 
| 292 |  |  |  |  |  |  | ); | 
| 293 |  |  |  |  |  |  | } | 
| 294 |  |  |  |  |  |  |  | 
| 295 |  |  |  |  |  |  | # now, we have to take all the values we just got and | 
| 296 |  |  |  |  |  |  | # collapse them to init args | 
| 297 | 233 |  |  |  |  | 493 | for my $element ( @{ $self->xml_elements } ) { | 
|  | 233 |  |  |  |  | 6764 |  | 
| 298 | 805 |  |  |  |  | 2574 | my $key = $element->name; | 
| 299 | 805 | 100 |  |  |  | 1830 | next unless exists $init_args{$key}; | 
| 300 | 273 |  |  |  |  | 411 | my $expect; | 
| 301 | 273 | 100 | 100 |  |  | 8985 | if ( $element->has_xml_max and $element->xml_max == 1 ) { | 
| 302 | 202 |  |  |  |  | 528 | $expect = \&as_item; | 
| 303 |  |  |  |  |  |  | } | 
| 304 |  |  |  |  |  |  | else { | 
| 305 | 71 |  |  |  |  | 261 | $expect = \&as_listref; | 
| 306 |  |  |  |  |  |  | } | 
| 307 |  |  |  |  |  |  | push @rv, eval { | 
| 308 | 273 |  |  |  |  | 840 | my $val = $expect->(delete $init_args{$key}); | 
| 309 | 273 | 50 |  |  |  | 8524 | if ( my $t_c = $element->type_constraint) { | 
| 310 | 273 | 50 |  |  |  | 4263 | if ( !$t_c->check($val) ) { | 
| 311 | 0 | 0 |  |  |  | 0 | if ( ref $val ) { | 
| 312 | 0 |  |  |  |  | 0 | $val = encode_json $val; | 
| 313 |  |  |  |  |  |  | } | 
| 314 | 0 |  |  |  |  | 0 | die "value '$val' failed type check"; | 
| 315 |  |  |  |  |  |  | } | 
| 316 |  |  |  |  |  |  | } | 
| 317 |  |  |  |  |  |  | ( ( (       $element->has_xml_nodeName_attr | 
| 318 |  |  |  |  |  |  | and | 
| 319 |  |  |  |  |  |  | exists $init_arg_names{$key} | 
| 320 |  |  |  |  |  |  | ) | 
| 321 |  |  |  |  |  |  | ? ( $element->xml_nodeName_attr => | 
| 322 |  |  |  |  |  |  | $expect->($init_arg_names{$key}) | 
| 323 |  |  |  |  |  |  | ) | 
| 324 |  |  |  |  |  |  | : () | 
| 325 |  |  |  |  |  |  | ), | 
| 326 |  |  |  |  |  |  | ( (     $element->has_xmlns_attr | 
| 327 |  |  |  |  |  |  | and | 
| 328 |  |  |  |  |  |  | exists $init_arg_xmlns{$key} | 
| 329 |  |  |  |  |  |  | ) | 
| 330 |  |  |  |  |  |  | ? ( $element->xmlns_attr => | 
| 331 | 273 | 100 | 66 |  |  | 32982 | $expect->($init_arg_xmlns{$key}) | 
|  |  | 100 | 66 |  |  |  |  | 
| 332 |  |  |  |  |  |  | ) | 
| 333 |  |  |  |  |  |  | : () | 
| 334 |  |  |  |  |  |  | ), | 
| 335 |  |  |  |  |  |  | $key => $val, | 
| 336 |  |  |  |  |  |  | ); | 
| 337 | 273 | 50 |  |  |  | 574 | } or do { | 
| 338 | 0 |  |  |  |  | 0 | my $err = $@; | 
| 339 | 0 |  |  |  |  | 0 | my $bad = $init_arg_nodes{$key}; | 
| 340 | 0 | 0 |  |  |  | 0 | if ( ref $bad eq "ARRAY" ) { | 
| 341 | 0 |  |  |  |  | 0 | $bad = $bad->parentNode; | 
| 342 |  |  |  |  |  |  | } | 
| 343 |  |  |  |  |  |  | else { | 
| 344 | 0 |  |  |  |  | 0 | $context = | 
| 345 |  |  |  |  |  |  | $context->next_ctx($bad->namespaceURI, | 
| 346 |  |  |  |  |  |  | $bad->localname); | 
| 347 |  |  |  |  |  |  | } | 
| 348 | 0 |  |  |  |  | 0 | $context->exception( | 
| 349 |  |  |  |  |  |  | "internal error: processing '$key' attribute: $err", | 
| 350 |  |  |  |  |  |  | $bad, | 
| 351 |  |  |  |  |  |  | ); | 
| 352 |  |  |  |  |  |  | }; | 
| 353 |  |  |  |  |  |  | } | 
| 354 | 233 | 50 |  |  |  | 887 | if (my @leftovers = keys %init_args) { | 
| 355 | 0 |  |  |  |  | 0 | $context->exception( | 
| 356 |  |  |  |  |  |  | "internal error: ".@leftovers | 
| 357 |  |  |  |  |  |  | ." init arg(s) left over (@leftovers)", | 
| 358 |  |  |  |  |  |  | ); | 
| 359 |  |  |  |  |  |  | } | 
| 360 | 233 |  |  |  |  | 1178 | return @rv; | 
| 361 |  |  |  |  |  |  | } | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | sub marshall_in_element { | 
| 364 | 259 |  |  | 259 | 1 | 2281 | my $self = shift; | 
| 365 |  |  |  |  |  |  |  | 
| 366 | 259 |  |  |  |  | 1721 | my ( $node, $ctx, $lax ) = pos_validated_list( | 
| 367 |  |  |  |  |  |  | \@_, | 
| 368 |  |  |  |  |  |  | { isa => 'XML::LibXML::Node' }, | 
| 369 |  |  |  |  |  |  | { isa => 'PRANG::Graph::Context' }, | 
| 370 |  |  |  |  |  |  | { isa => 'Bool', optional => 1, default => 0 }, | 
| 371 |  |  |  |  |  |  | ); | 
| 372 |  |  |  |  |  |  |  | 
| 373 | 259 |  |  |  |  | 79949 | my @node_attr = grep { $_->isa("XML::LibXML::Attr") } | 
|  | 153 |  |  |  |  | 1460 |  | 
| 374 |  |  |  |  |  |  | $node->attributes; | 
| 375 | 259 |  |  |  |  | 2411 | my @ns_attr = $node->getNamespaces; | 
| 376 |  |  |  |  |  |  |  | 
| 377 | 259 | 100 |  |  |  | 800 | if (@ns_attr) { | 
| 378 |  |  |  |  |  |  | $ctx->add_xmlns($_->declaredPrefix//"" => $_->declaredURI) | 
| 379 | 28 |  | 100 |  |  | 351 | for @ns_attr; | 
| 380 |  |  |  |  |  |  | } | 
| 381 |  |  |  |  |  |  |  | 
| 382 | 259 |  |  |  |  | 1643 | my $new_ctx = $ctx->next_ctx( | 
| 383 |  |  |  |  |  |  | $node->namespaceURI, | 
| 384 |  |  |  |  |  |  | $node->localname, | 
| 385 |  |  |  |  |  |  | ); | 
| 386 |  |  |  |  |  |  |  | 
| 387 | 259 |  |  |  |  | 1077 | my @init_args = $self->accept_attributes( \@node_attr, $new_ctx, $lax ); | 
| 388 |  |  |  |  |  |  |  | 
| 389 |  |  |  |  |  |  | # now process elements | 
| 390 |  |  |  |  |  |  | my @childNodes = grep { | 
| 391 | 257 |  | 66 |  |  | 949 | !(  $_->isa("XML::LibXML::Comment") | 
|  | 789 |  |  |  |  | 9033 |  | 
| 392 |  |  |  |  |  |  | or | 
| 393 |  |  |  |  |  |  | $_->isa("XML::LibXML::Text") and $_->data =~ /\A\s+\Z/ | 
| 394 |  |  |  |  |  |  | ) | 
| 395 |  |  |  |  |  |  | } $node->childNodes; | 
| 396 |  |  |  |  |  |  |  | 
| 397 | 257 |  |  |  |  | 2032 | push @init_args, $self->accept_childnodes( \@childNodes, $new_ctx, $lax ); | 
| 398 |  |  |  |  |  |  |  | 
| 399 | 227 |  |  |  |  | 432 | my $value = eval { $self->name->new(@init_args) }; | 
|  | 227 |  |  |  |  | 1249 |  | 
| 400 | 227 | 50 |  |  |  | 255935 | if ( !$value ) { | 
| 401 | 0 |  |  |  |  | 0 | my $error = $@; | 
| 402 | 0 |  |  |  |  | 0 | $error =~ m|^(.+) at /|; | 
| 403 | 0 |  |  |  |  | 0 | my $msg = $1; | 
| 404 | 0 |  |  |  |  | 0 | $ctx->exception( | 
| 405 |  |  |  |  |  |  | "Validation error from ".$self->name | 
| 406 |  |  |  |  |  |  | ." constructor: $1", | 
| 407 |  |  |  |  |  |  | $node, | 
| 408 |  |  |  |  |  |  | ); | 
| 409 |  |  |  |  |  |  | } | 
| 410 |  |  |  |  |  |  | else { | 
| 411 | 227 |  |  |  |  | 1090 | return $value; | 
| 412 |  |  |  |  |  |  | } | 
| 413 |  |  |  |  |  |  | } | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | sub add_xml_attr { | 
| 416 | 144 |  |  | 144 | 1 | 6420 | my $self = shift; | 
| 417 |  |  |  |  |  |  |  | 
| 418 | 144 |  |  |  |  | 697 | my ( $item, $node, $ctx ) = pos_validated_list( | 
| 419 |  |  |  |  |  |  | \@_, | 
| 420 |  |  |  |  |  |  | { isa => 'Object' }, | 
| 421 |  |  |  |  |  |  | { isa => 'XML::LibXML::Element' }, | 
| 422 |  |  |  |  |  |  | { isa => 'PRANG::Graph::Context' }, | 
| 423 |  |  |  |  |  |  | ); | 
| 424 |  |  |  |  |  |  |  | 
| 425 |  |  |  |  |  |  |  | 
| 426 | 144 |  |  |  |  | 47774 | my $attributes = $self->xml_attr; | 
| 427 | 144 |  |  |  |  | 749 | while ( my ($xmlns, $att) = each %$attributes ) { | 
| 428 | 117 |  |  |  |  | 710 | while ( my ($attName, $meta_att) = each %$att ) { | 
| 429 | 97 |  |  |  |  | 505 | my $is_optional; | 
| 430 | 97 |  |  |  |  | 350 | my $obj_att_name = $meta_att->name; | 
| 431 | 97 | 100 |  |  |  | 3443 | if ( $meta_att->has_xml_required ) { | 
|  |  | 100 |  |  |  |  |  | 
| 432 | 48 |  |  |  |  | 1407 | $is_optional = !$meta_att->xml_required; | 
| 433 |  |  |  |  |  |  | } | 
| 434 |  |  |  |  |  |  | elsif ( !$meta_att->is_required ) { | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | # it's optional | 
| 437 | 47 |  |  |  |  | 458 | $is_optional = 1; | 
| 438 |  |  |  |  |  |  | } | 
| 439 |  |  |  |  |  |  |  | 
| 440 |  |  |  |  |  |  | # we /could/ use $meta_att->get_value($item) | 
| 441 |  |  |  |  |  |  | # here, but I consider that to break | 
| 442 |  |  |  |  |  |  | # encapsulation | 
| 443 | 97 |  |  |  |  | 822 | my $value = $item->$obj_att_name; | 
| 444 | 97 |  |  |  |  | 643 | my $xml_att_name = $attName; | 
| 445 | 97 | 100 |  |  |  | 3133 | if ( $meta_att->has_xml_name ) { | 
| 446 | 43 |  |  |  |  | 1435 | my $method = $meta_att->has_xmlns_attr; | 
| 447 | 43 |  |  |  |  | 104 | $xml_att_name = $attName; | 
| 448 |  |  |  |  |  |  | } | 
| 449 | 97 | 100 |  |  |  | 3082 | if ( $meta_att->has_xmlns_attr ) { | 
| 450 | 24 |  |  |  |  | 746 | my $method = $meta_att->xmlns_attr; | 
| 451 | 24 |  |  |  |  | 661 | $xmlns = $item->$method; | 
| 452 |  |  |  |  |  |  | } | 
| 453 | 97 | 100 |  |  |  | 395 | if ( !defined $value ) { | 
| 454 | 43 | 50 |  |  |  | 139 | die "could not serialize $item; slot " | 
| 455 |  |  |  |  |  |  | .$meta_att->name." empty" | 
| 456 |  |  |  |  |  |  | unless $is_optional; | 
| 457 | 43 |  |  |  |  | 273 | next; | 
| 458 |  |  |  |  |  |  | } | 
| 459 |  |  |  |  |  |  |  | 
| 460 |  |  |  |  |  |  | my $emit_att = sub { | 
| 461 | 68 |  |  | 68 |  | 210 | my ($xmlns, $name, $value) = @_; | 
| 462 | 68 |  |  |  |  | 101 | my $prefix; | 
| 463 | 68 | 100 |  |  |  | 139 | if ($xmlns) { | 
| 464 | 14 |  |  |  |  | 66 | $prefix = $ctx->get_prefix( | 
| 465 |  |  |  |  |  |  | $xmlns, $item, $node, | 
| 466 |  |  |  |  |  |  | ); | 
| 467 | 14 | 50 |  |  |  | 47 | if ( length $prefix ) { | 
| 468 | 14 |  |  |  |  | 30 | $prefix .= ":"; | 
| 469 |  |  |  |  |  |  | } | 
| 470 |  |  |  |  |  |  | } | 
| 471 |  |  |  |  |  |  | else { | 
| 472 | 54 |  |  |  |  | 89 | $prefix = ""; | 
| 473 |  |  |  |  |  |  | } | 
| 474 | 68 |  |  |  |  | 274 | $node->setAttribute( | 
| 475 |  |  |  |  |  |  | $prefix.$name, $value, | 
| 476 |  |  |  |  |  |  | ); | 
| 477 | 54 |  |  |  |  | 311 | }; | 
| 478 |  |  |  |  |  |  |  | 
| 479 |  |  |  |  |  |  | my $do_array = sub { | 
| 480 | 5 |  |  | 5 |  | 12 | my $att_name = shift; | 
| 481 | 5 |  |  |  |  | 10 | my $array = shift; | 
| 482 | 5 |  |  |  |  | 11 | my $xmlns = shift; | 
| 483 | 5 |  |  |  |  | 18 | for ( my $i = 0; $i <= $#$array; $i++ ) { | 
| 484 | 15 |  | 66 |  |  | 197 | $emit_att->( | 
| 485 |  |  |  |  |  |  | $xmlns&&$xmlns->[$i], | 
| 486 |  |  |  |  |  |  | $att_name, | 
| 487 |  |  |  |  |  |  | $array->[$i], | 
| 488 |  |  |  |  |  |  | ); | 
| 489 |  |  |  |  |  |  | } | 
| 490 | 54 |  |  |  |  | 237 | }; | 
| 491 |  |  |  |  |  |  |  | 
| 492 | 54 | 100 |  |  |  | 197 | if ( ref $value eq "HASH" ) { | 
|  |  | 100 |  |  |  |  |  | 
| 493 |  |  |  |  |  |  |  | 
| 494 |  |  |  |  |  |  | # wildcarded attribute name case | 
| 495 | 11 |  |  |  |  | 65 | while ( my ($att, $val) = each %$value ) { | 
| 496 | 15 |  |  |  |  | 80 | my $att_xmlns; | 
| 497 | 15 | 100 |  |  |  | 37 | if ($xmlns) { | 
| 498 | 12 |  |  |  |  | 35 | $att_xmlns = $xmlns->{$att}; | 
| 499 |  |  |  |  |  |  | } | 
| 500 |  |  |  |  |  |  |  | 
| 501 |  |  |  |  |  |  | # now, we can *still* have arrays here.. | 
| 502 | 15 | 100 |  |  |  | 51 | if ( ref $val eq "ARRAY" ) { | 
| 503 | 4 |  |  |  |  | 14 | $do_array->( | 
| 504 |  |  |  |  |  |  | $att, $val, | 
| 505 |  |  |  |  |  |  | $att_xmlns, | 
| 506 |  |  |  |  |  |  | ); | 
| 507 |  |  |  |  |  |  | } | 
| 508 |  |  |  |  |  |  | else { | 
| 509 | 11 |  |  |  |  | 30 | $emit_att->( | 
| 510 |  |  |  |  |  |  | $att_xmlns, | 
| 511 |  |  |  |  |  |  | $att, $val, | 
| 512 |  |  |  |  |  |  | ); | 
| 513 |  |  |  |  |  |  | } | 
| 514 |  |  |  |  |  |  | } | 
| 515 |  |  |  |  |  |  | } | 
| 516 |  |  |  |  |  |  | elsif ( ref $value eq "ARRAY" ) { | 
| 517 | 1 |  |  |  |  | 8 | $do_array->( | 
| 518 |  |  |  |  |  |  | $xml_att_name, | 
| 519 |  |  |  |  |  |  | $value, | 
| 520 |  |  |  |  |  |  | $xmlns, | 
| 521 |  |  |  |  |  |  | ); | 
| 522 |  |  |  |  |  |  | } | 
| 523 |  |  |  |  |  |  | else { | 
| 524 | 42 |  |  |  |  | 92 | $emit_att->( $xmlns, $xml_att_name, $value ); | 
| 525 |  |  |  |  |  |  | } | 
| 526 |  |  |  |  |  |  | } | 
| 527 |  |  |  |  |  |  | } | 
| 528 |  |  |  |  |  |  | } | 
| 529 |  |  |  |  |  |  |  | 
| 530 |  |  |  |  |  |  | sub to_libxml { | 
| 531 | 142 |  |  | 142 | 1 | 2074 | my $self = shift; | 
| 532 |  |  |  |  |  |  |  | 
| 533 | 142 |  |  |  |  | 848 | my ( $item, $node, $ctx ) = pos_validated_list( | 
| 534 |  |  |  |  |  |  | \@_, | 
| 535 |  |  |  |  |  |  | { isa => 'Object' }, | 
| 536 |  |  |  |  |  |  | { isa => 'XML::LibXML::Element' }, | 
| 537 |  |  |  |  |  |  | { isa => 'PRANG::Graph::Context' }, | 
| 538 |  |  |  |  |  |  | ); | 
| 539 |  |  |  |  |  |  |  | 
| 540 |  |  |  |  |  |  |  | 
| 541 | 142 |  |  |  |  | 45843 | $self->add_xml_attr($item, $node, $ctx); | 
| 542 | 142 |  |  |  |  | 4691 | $self->graph->output($item, $node, $ctx); | 
| 543 |  |  |  |  |  |  | } | 
| 544 |  |  |  |  |  |  |  | 
| 545 |  |  |  |  |  |  | package Moose::Meta::Class::Custom::Trait::PRANG; | 
| 546 |  |  |  |  |  |  | $Moose::Meta::Class::Custom::Trait::PRANG::VERSION = '0.21'; | 
| 547 | 0 |  |  | 0 |  |  | sub register_implementation {"PRANG::Graph::Meta::Class"} | 
| 548 |  |  |  |  |  |  |  | 
| 549 |  |  |  |  |  |  | 1; | 
| 550 |  |  |  |  |  |  |  | 
| 551 |  |  |  |  |  |  | __END__ | 
| 552 |  |  |  |  |  |  |  | 
| 553 |  |  |  |  |  |  | =head1 NAME | 
| 554 |  |  |  |  |  |  |  | 
| 555 |  |  |  |  |  |  | PRANG::Graph::Meta::Class - metaclass metarole for PRANG-enabled classes | 
| 556 |  |  |  |  |  |  |  | 
| 557 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 558 |  |  |  |  |  |  |  | 
| 559 |  |  |  |  |  |  | package MyClass; | 
| 560 |  |  |  |  |  |  | use Moose; | 
| 561 |  |  |  |  |  |  | use PRANG::Graph; | 
| 562 |  |  |  |  |  |  |  | 
| 563 |  |  |  |  |  |  | # - or - | 
| 564 |  |  |  |  |  |  | package MyClass; | 
| 565 |  |  |  |  |  |  | use Moose -traits => ["PRANG"]; | 
| 566 |  |  |  |  |  |  |  | 
| 567 |  |  |  |  |  |  | # - or - | 
| 568 |  |  |  |  |  |  | package MyClass; | 
| 569 |  |  |  |  |  |  | use Moose; | 
| 570 |  |  |  |  |  |  | PRANG::Graph::Meta::Class->meta->apply(__PACKAGE__->meta); | 
| 571 |  |  |  |  |  |  |  | 
| 572 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 573 |  |  |  |  |  |  |  | 
| 574 |  |  |  |  |  |  | This role defines class properties and methods for PRANG classes' meta | 
| 575 |  |  |  |  |  |  | objects.  ie, the methods it defines are all to be found in | 
| 576 |  |  |  |  |  |  | C<YourClass-E<gt>meta>, not C<YourClass>. | 
| 577 |  |  |  |  |  |  |  | 
| 578 |  |  |  |  |  |  | The L<PRANG::Graph::Meta::Class> object is the centre of the state | 
| 579 |  |  |  |  |  |  | machine which defines the parsing and emitting rules for your classes. | 
| 580 |  |  |  |  |  |  | In other words, the I<XML Graph> (see L<PRANG>).  Each one corresponds | 
| 581 |  |  |  |  |  |  | to an XML element (though not all XML elements will require a full | 
| 582 |  |  |  |  |  |  | object class), and via these objects can be found the lists of | 
| 583 |  |  |  |  |  |  | elements and attributes which define the XML structure. | 
| 584 |  |  |  |  |  |  |  | 
| 585 |  |  |  |  |  |  | =head1 ATTRIBUTES | 
| 586 |  |  |  |  |  |  |  | 
| 587 |  |  |  |  |  |  | =over | 
| 588 |  |  |  |  |  |  |  | 
| 589 |  |  |  |  |  |  | =item B<HashRef[HashRef[PRANG::Graph::Meta::Attr]] xml_attr> | 
| 590 |  |  |  |  |  |  |  | 
| 591 |  |  |  |  |  |  | This read-only property maps from XML namespace and localname to a | 
| 592 |  |  |  |  |  |  | L<PRANG::Graph::Meta::Attr> object, defining the type of that | 
| 593 |  |  |  |  |  |  | attribute and other things described on its perldoc. | 
| 594 |  |  |  |  |  |  |  | 
| 595 |  |  |  |  |  |  | The first time it is accessed, it is built - so be sure to carry out | 
| 596 |  |  |  |  |  |  | any run-time meta magic before parsing or emitting objects of that | 
| 597 |  |  |  |  |  |  | type. | 
| 598 |  |  |  |  |  |  |  | 
| 599 |  |  |  |  |  |  | =item B<ArrayRef[PRANG::Graph::Meta::Element] xml_elements> | 
| 600 |  |  |  |  |  |  |  | 
| 601 |  |  |  |  |  |  | This contains an ordered list of all of the XML elements which exist | 
| 602 |  |  |  |  |  |  | in this class.  See L<PRANG::Graph::Meta::Element>. | 
| 603 |  |  |  |  |  |  |  | 
| 604 |  |  |  |  |  |  | Like C<xml_attr>, the first time it is accessed it is built.  There | 
| 605 |  |  |  |  |  |  | are currently some problems with ordering and role composition; as the | 
| 606 |  |  |  |  |  |  | ordering of elements is returned from a moose accessor, but when | 
| 607 |  |  |  |  |  |  | composing roles into classes, they are applied in any order. | 
| 608 |  |  |  |  |  |  |  | 
| 609 |  |  |  |  |  |  | =item B<PRANG::Graph::Node graph> | 
| 610 |  |  |  |  |  |  |  | 
| 611 |  |  |  |  |  |  | The C<graph> property is the acceptor and emitter for the child nodes | 
| 612 |  |  |  |  |  |  | of this class.  See L<PRANG::Graph::Node> for the low-down.  This is | 
| 613 |  |  |  |  |  |  | constructed by a transform on the B<xml_elements> property. | 
| 614 |  |  |  |  |  |  |  | 
| 615 |  |  |  |  |  |  | =back | 
| 616 |  |  |  |  |  |  |  | 
| 617 |  |  |  |  |  |  | =head1 METHODS | 
| 618 |  |  |  |  |  |  |  | 
| 619 |  |  |  |  |  |  | =head2 B<accept_attributes(\@node_attr, $ctx)> | 
| 620 |  |  |  |  |  |  |  | 
| 621 |  |  |  |  |  |  | =head2 B<accept_childnodes(\@childNodes, $ctx)> | 
| 622 |  |  |  |  |  |  |  | 
| 623 |  |  |  |  |  |  | =head2 B<marshall_in_element($node, $ctx)> | 
| 624 |  |  |  |  |  |  |  | 
| 625 |  |  |  |  |  |  | These methods are the parsing machinery, their API is quite subject to | 
| 626 |  |  |  |  |  |  | change; the methods provided by the L<PRANG::Graph> role are what you | 
| 627 |  |  |  |  |  |  | should be using, unless you are writing a PRANG extension. | 
| 628 |  |  |  |  |  |  |  | 
| 629 |  |  |  |  |  |  | =head2 B<add_xml_attr($item, $node, $ctx)> | 
| 630 |  |  |  |  |  |  |  | 
| 631 |  |  |  |  |  |  | =head2 B<to_libxml($item, $node, $ctx)> | 
| 632 |  |  |  |  |  |  |  | 
| 633 |  |  |  |  |  |  | Similarly, these are the emitting methods. | 
| 634 |  |  |  |  |  |  |  | 
| 635 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 636 |  |  |  |  |  |  |  | 
| 637 |  |  |  |  |  |  | L<PRANG::Graph::Meta::Attr>, L<PRANG::Graph::Meta::Element>, | 
| 638 |  |  |  |  |  |  | L<PRANG::Graph::Node> | 
| 639 |  |  |  |  |  |  |  | 
| 640 |  |  |  |  |  |  | =head1 AUTHOR AND LICENCE | 
| 641 |  |  |  |  |  |  |  | 
| 642 |  |  |  |  |  |  | Development commissioned by NZ Registry Services, and carried out by | 
| 643 |  |  |  |  |  |  | Catalyst IT - L<http://www.catalyst.net.nz/> | 
| 644 |  |  |  |  |  |  |  | 
| 645 |  |  |  |  |  |  | Copyright 2009, 2010, NZ Registry Services.  This module is licensed | 
| 646 |  |  |  |  |  |  | under the Artistic License v2.0, which permits relicensing under other | 
| 647 |  |  |  |  |  |  | Free Software licenses. | 
| 648 |  |  |  |  |  |  |  | 
| 649 |  |  |  |  |  |  | =cut | 
| 650 |  |  |  |  |  |  |  |