| line | stmt | bran | cond | sub | pod | time | code | 
| 1 | 50 |  |  | 50 |  | 520 | use v5.14; | 
|  | 50 |  |  |  |  | 167 |  | 
| 2 | 50 |  |  | 50 |  | 237 | use warnings; | 
|  | 50 |  |  |  |  | 114 |  | 
|  | 50 |  |  |  |  | 1518 |  | 
| 3 |  |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  | =head1 NAME | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | Attean::API::Binding - Name to term bindings | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | =head1 VERSION | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | This document describes Attean::API::Binding version 0.033 | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | The Attean::API::Binding role defines a common API for all objects that map | 
| 15 |  |  |  |  |  |  | names to L<Attean::API::Term> objects. This includes triples, quads, and | 
| 16 |  |  |  |  |  |  | SPARQL results (variable bindings). | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | =head1 REQUIRED METHODS | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | Classes consuming this role must provide the following methods: | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | =over 4 | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | =item C<< value( $name ) >> | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | Returns the L<Attean::API::Term> object mapped to the variable named C<< $name >>, | 
| 27 |  |  |  |  |  |  | or C<< undef >> if no such term is mapped. | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  | =item C<< variables >> | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | Returns a list of the variable names mapped to L<Attean::API::Term> objects in | 
| 32 |  |  |  |  |  |  | this mapping. | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | =item C<< apply_map( $mapper ) >> | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | Returns a new mapping object (of the same class as the referent) with term | 
| 37 |  |  |  |  |  |  | objects rewritten using the supplied L<Attean::Mapper> object C<< $mapper >>. | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | =back | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | =head1 METHODS | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | This role provides default implementations of the following methods: | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | =over 4 | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | =item C<< mapping >> | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | Returns a HASH mapping variable names to L<Attean::API::Term> objects. | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | =item C<< values >> | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | Returns a list of L<Attean::API::Term> objects corresponding to the variable | 
| 54 |  |  |  |  |  |  | names returned by the referent's C<< variables >> method. | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | =item C<< tuples_string >> | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | Returns a string serialization of the L<Attean::API::Term> objects in the order | 
| 59 |  |  |  |  |  |  | they are returned by the referent's C<< values >> method. | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | =item C<< as_string >> | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | Returns a string serialization of the variable bindings. | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | =item C<< has_blanks >> | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | Returns true if any variable is bound to an L<Attean::API::Blank> term, false | 
| 68 |  |  |  |  |  |  | otherwise. | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | =cut | 
| 71 |  |  |  |  |  |  |  | 
| 72 | 50 |  |  | 50 |  | 240 | use Type::Tiny::Role; | 
|  | 50 |  |  |  |  | 105 |  | 
|  | 50 |  |  |  |  | 2002 |  | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | use Scalar::Util qw(blessed); | 
| 75 | 50 |  |  | 50 |  | 276 | use List::MoreUtils qw(zip); | 
|  | 50 |  |  |  |  | 125 |  | 
|  | 50 |  |  |  |  | 2623 |  | 
| 76 | 50 |  |  | 50 |  | 345 |  | 
|  | 50 |  |  |  |  | 98 |  | 
|  | 50 |  |  |  |  | 567 |  | 
| 77 |  |  |  |  |  |  | use Moo::Role; | 
| 78 | 50 |  |  | 50 |  | 30855 |  | 
|  | 50 |  |  |  |  | 98 |  | 
|  | 50 |  |  |  |  | 303 |  | 
| 79 |  |  |  |  |  |  | requires 'value'; | 
| 80 |  |  |  |  |  |  | requires 'variables'; | 
| 81 |  |  |  |  |  |  | requires 'apply_map'; | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | my $self	= shift; | 
| 84 |  |  |  |  |  |  | my @k		= $self->variables; | 
| 85 | 61 |  |  | 61 | 1 | 2335 | my @v		= $self->values; | 
| 86 | 61 |  |  |  |  | 95 | return zip @k, @v; | 
| 87 | 61 |  |  |  |  | 94 | } | 
| 88 | 61 |  |  |  |  | 336 |  | 
| 89 |  |  |  |  |  |  | my $self	= shift; | 
| 90 |  |  |  |  |  |  | return map { $self->value($_) } $self->variables; | 
| 91 |  |  |  |  |  |  | } | 
| 92 | 2820 |  |  | 2820 | 1 | 22833 |  | 
| 93 | 2820 |  |  |  |  | 4902 | my $self	= shift; | 
|  | 9732 |  |  |  |  | 14382 |  | 
| 94 |  |  |  |  |  |  | my @strs	= map { $_->ntriples_string } $self->values; | 
| 95 |  |  |  |  |  |  | return join(' ', @strs) . ' .'; | 
| 96 |  |  |  |  |  |  | } | 
| 97 | 404 |  |  | 404 | 1 | 2447 | shift->tuples_string(); | 
| 98 | 404 |  |  |  |  | 759 | } | 
|  | 1448 |  |  |  |  | 28855 |  | 
| 99 | 404 |  |  |  |  | 7794 |  | 
| 100 |  |  |  |  |  |  | my $self	= shift; | 
| 101 |  |  |  |  |  |  | foreach my $term ($self->values) { | 
| 102 | 267 |  |  | 267 | 1 | 13867 | return 1 if ($term->does('Attean::API::Blank')); | 
| 103 |  |  |  |  |  |  | if ($term->does('Attean::API::Binding')) { | 
| 104 |  |  |  |  |  |  | return 1 if ($term->has_blanks); | 
| 105 |  |  |  |  |  |  | } | 
| 106 | 56 |  |  | 56 | 1 | 85 | } | 
| 107 | 56 |  |  |  |  | 123 | return 0; | 
| 108 | 94 | 100 |  |  |  | 798 | } | 
| 109 | 55 | 50 |  |  |  | 969 |  | 
| 110 | 0 | 0 |  |  |  | 0 | =item C<< sameTerms( $other ) >> | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | =cut | 
| 113 | 17 |  |  |  |  | 336 |  | 
| 114 |  |  |  |  |  |  | my $self	= shift; | 
| 115 |  |  |  |  |  |  | my $other	= shift; | 
| 116 |  |  |  |  |  |  | return 0 unless ($other->does('Attean::API::Binding')); | 
| 117 |  |  |  |  |  |  | my @variables	= sort $self->variables; | 
| 118 |  |  |  |  |  |  | my @other_vars	= sort $other->variables; | 
| 119 |  |  |  |  |  |  | return 0 unless (scalar(@variables) == scalar(@other_vars)); | 
| 120 |  |  |  |  |  |  | foreach my $i (0 .. $#variables) { | 
| 121 | 0 |  |  | 0 | 1 | 0 | return 0 unless $variables[$i] eq $other_vars[$i]; | 
| 122 | 0 |  |  |  |  | 0 | } | 
| 123 | 0 | 0 |  |  |  | 0 |  | 
| 124 | 0 |  |  |  |  | 0 | foreach my $v (@variables) { | 
| 125 | 0 |  |  |  |  | 0 | my $value		= $self->value($v); | 
| 126 | 0 | 0 |  |  |  | 0 | my $other_value	= $other->value($v); | 
| 127 | 0 |  |  |  |  | 0 | if ($value->does('Attean::API::Binding')) { | 
| 128 | 0 | 0 |  |  |  | 0 | return 0 unless $value->sameTerms($other_value); | 
| 129 |  |  |  |  |  |  | } else { | 
| 130 |  |  |  |  |  |  | return 0 unless ($value->equals($other_value)); | 
| 131 | 0 |  |  |  |  | 0 | } | 
| 132 | 0 |  |  |  |  | 0 | } | 
| 133 | 0 |  |  |  |  | 0 | return 1; | 
| 134 | 0 | 0 |  |  |  | 0 | } | 
| 135 | 0 | 0 |  |  |  | 0 |  | 
| 136 |  |  |  |  |  |  | =item C<< equals( $other ) >> | 
| 137 | 0 | 0 |  |  |  | 0 |  | 
| 138 |  |  |  |  |  |  | =cut | 
| 139 |  |  |  |  |  |  |  | 
| 140 | 0 |  |  |  |  | 0 | my $self	= shift; | 
| 141 |  |  |  |  |  |  | my $other	= shift; | 
| 142 |  |  |  |  |  |  | return 0 unless ($other->does('Attean::API::Binding')); | 
| 143 |  |  |  |  |  |  | my @variables	= sort $self->variables; | 
| 144 |  |  |  |  |  |  | my @other_vars	= sort $other->variables; | 
| 145 |  |  |  |  |  |  | unless (scalar(@variables) == scalar(@other_vars)) { | 
| 146 |  |  |  |  |  |  | return 0; | 
| 147 |  |  |  |  |  |  | } | 
| 148 | 0 |  |  | 0 | 1 | 0 | foreach my $i (0 .. $#variables) { | 
| 149 | 0 |  |  |  |  | 0 | unless ($variables[$i] eq $other_vars[$i]) { | 
| 150 | 0 | 0 |  |  |  | 0 | return 0; | 
| 151 | 0 |  |  |  |  | 0 | } | 
| 152 | 0 |  |  |  |  | 0 | } | 
| 153 | 0 | 0 |  |  |  | 0 |  | 
| 154 | 0 |  |  |  |  | 0 | foreach my $v (@variables) { | 
| 155 |  |  |  |  |  |  | my $value		= $self->value($v); | 
| 156 | 0 |  |  |  |  | 0 | my $other_value	= $other->value($v); | 
| 157 | 0 | 0 |  |  |  | 0 | if ($value->does('Attean::API::Binding')) { | 
| 158 | 0 |  |  |  |  | 0 | unless ($value->equals($other_value)) { | 
| 159 |  |  |  |  |  |  | return 0; | 
| 160 |  |  |  |  |  |  | } | 
| 161 |  |  |  |  |  |  | } else { | 
| 162 | 0 |  |  |  |  | 0 | unless (0 == $value->compare($other_value)) { | 
| 163 | 0 |  |  |  |  | 0 | return 0; | 
| 164 | 0 |  |  |  |  | 0 | } | 
| 165 | 0 | 0 |  |  |  | 0 | } | 
| 166 | 0 | 0 |  |  |  | 0 | } | 
| 167 | 0 |  |  |  |  | 0 | return 1; | 
| 168 |  |  |  |  |  |  | } | 
| 169 |  |  |  |  |  |  |  | 
| 170 | 0 | 0 |  |  |  | 0 | =item C<< blanks >> | 
| 171 | 0 |  |  |  |  | 0 |  | 
| 172 |  |  |  |  |  |  | Returns all the values in this mapping (recursively, if any values are embedded | 
| 173 |  |  |  |  |  |  | bindings) that are blank nodes. | 
| 174 |  |  |  |  |  |  |  | 
| 175 | 0 |  |  |  |  | 0 | =cut | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | my $self	= shift; | 
| 178 |  |  |  |  |  |  | my %nodes; | 
| 179 |  |  |  |  |  |  | foreach my $term ($self->values) { | 
| 180 |  |  |  |  |  |  | if ($term->does('Attean::API::Blank')) { | 
| 181 |  |  |  |  |  |  | $nodes{ $term->value }	= $term; | 
| 182 |  |  |  |  |  |  | } | 
| 183 |  |  |  |  |  |  | if ($term->does('Attean::API::Binding')) { | 
| 184 |  |  |  |  |  |  | foreach my $b ($term->blanks) { | 
| 185 |  |  |  |  |  |  | $nodes{ $b->value }	= $b; | 
| 186 | 30 |  |  | 30 | 1 | 42 | } | 
| 187 | 30 |  |  |  |  | 39 | } | 
| 188 | 30 |  |  |  |  | 61 | } | 
| 189 | 110 | 100 |  |  |  | 1316 | return CORE::values %nodes; | 
| 190 | 40 |  |  |  |  | 546 | } | 
| 191 |  |  |  |  |  |  |  | 
| 192 | 110 | 50 |  |  |  | 1252 | =item C<< referenced_variables >> | 
| 193 | 0 |  |  |  |  | 0 |  | 
| 194 | 0 |  |  |  |  | 0 | Returns a list of the names of any variable values that are referenced in this | 
| 195 |  |  |  |  |  |  | binding (recursively, if any values are embedded bindings). | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | =cut | 
| 198 | 30 |  |  |  |  | 516 |  | 
| 199 |  |  |  |  |  |  | my $self	= shift; | 
| 200 |  |  |  |  |  |  | my %vars; | 
| 201 |  |  |  |  |  |  | foreach my $v ($self->values) { | 
| 202 |  |  |  |  |  |  | if ($v->does('Attean::API::Variable')) { | 
| 203 |  |  |  |  |  |  | $vars{$v->value}++; | 
| 204 |  |  |  |  |  |  | } elsif ($v->does('Attean::API::Binding')) { | 
| 205 |  |  |  |  |  |  | foreach my $name ($v->referenced_variables) { | 
| 206 |  |  |  |  |  |  | $vars{$name}++; | 
| 207 |  |  |  |  |  |  | } | 
| 208 |  |  |  |  |  |  | } | 
| 209 | 104 |  |  | 104 | 1 | 127 | } | 
| 210 | 104 |  |  |  |  | 131 | return keys %vars; | 
| 211 | 104 |  |  |  |  | 203 | } | 
| 212 | 312 | 100 |  |  |  | 3290 |  | 
|  |  | 100 |  |  |  |  |  | 
| 213 | 138 |  |  |  |  | 1844 | =item C<< is_ground >> | 
| 214 |  |  |  |  |  |  |  | 
| 215 | 13 |  |  |  |  | 349 | Returns tue is all the bound values consume L<Attean::API::Term>, false otherwise. | 
| 216 | 13 |  |  |  |  | 30 |  | 
| 217 |  |  |  |  |  |  | =cut | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | my $self	= shift; | 
| 220 | 104 |  |  |  |  | 1889 | my @non_terms	= grep { not($_->does('Attean::API::Term')) } $self->values; | 
| 221 |  |  |  |  |  |  | my @bad			= grep { not($_->does('Attean::API::Binding') and $_->is_ground) } @non_terms; | 
| 222 |  |  |  |  |  |  | return (scalar(@bad) == 0); | 
| 223 |  |  |  |  |  |  | } | 
| 224 |  |  |  |  |  |  |  | 
| 225 |  |  |  |  |  |  | =item C<< values_consuming_role( $role ) >> | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | Returns the list of bound values that consume C<< $role >>. | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | =cut | 
| 230 | 164 |  |  | 164 | 1 | 274 |  | 
| 231 | 164 |  |  |  |  | 364 | my $self	= shift; | 
|  | 632 |  |  |  |  | 6076 |  | 
| 232 | 164 |  | 33 |  |  | 1915 | my $role	= shift; | 
|  | 17 |  |  |  |  | 159 |  | 
| 233 | 164 |  |  |  |  | 2977 | return grep { $_->does($role) } $self->values; | 
| 234 |  |  |  |  |  |  | } | 
| 235 |  |  |  |  |  |  |  | 
| 236 |  |  |  |  |  |  | =item C<< tree_attributes >> | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | Returns the variables which are bound in this object. | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | =cut | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | my $self	= shift; | 
| 243 | 294 |  |  | 294 | 1 | 684 | return $self->variables; | 
| 244 | 294 |  |  |  |  | 365 | } | 
| 245 | 294 |  |  |  |  | 477 |  | 
|  | 882 |  |  |  |  | 9156 |  | 
| 246 |  |  |  |  |  |  | =item C<< apply_bindings( $binding ) >> | 
| 247 |  |  |  |  |  |  |  | 
| 248 |  |  |  |  |  |  | Construct a new binding by replacing variables with their bound values from | 
| 249 |  |  |  |  |  |  | C<< $binding >>. | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | =cut | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | my $self	= shift; | 
| 254 |  |  |  |  |  |  | my $class	= ref($self); | 
| 255 | 2 |  |  | 2 | 1 | 5 | my $bind	= shift; | 
| 256 | 2 |  |  |  |  | 7 | my %data; | 
| 257 |  |  |  |  |  |  | foreach my $k ($self->variables) { | 
| 258 |  |  |  |  |  |  | my $v	= $self->value($k); | 
| 259 |  |  |  |  |  |  | if ($v->does('Attean::API::TriplePattern')) { | 
| 260 |  |  |  |  |  |  | my $replace	= $v->apply_bindings($bind); | 
| 261 |  |  |  |  |  |  | $data{ $k }	= $replace; | 
| 262 |  |  |  |  |  |  | } elsif ($v->does('Attean::API::Variable')) { | 
| 263 |  |  |  |  |  |  | my $name	= $v->value; | 
| 264 |  |  |  |  |  |  | my $replace	= $bind->value($name); | 
| 265 |  |  |  |  |  |  | if (defined($replace) and blessed($replace)) { | 
| 266 |  |  |  |  |  |  | $data{ $k }	= $replace; | 
| 267 | 0 |  |  | 0 | 1 | 0 | } else { | 
| 268 | 0 |  |  |  |  | 0 | $data{ $k }	= $v; | 
| 269 | 0 |  |  |  |  | 0 | } | 
| 270 | 0 |  |  |  |  | 0 | } else { | 
| 271 | 0 |  |  |  |  | 0 | $data{ $k }	= $v; | 
| 272 | 0 |  |  |  |  | 0 | } | 
| 273 | 0 | 0 |  |  |  | 0 | } | 
|  |  | 0 |  |  |  |  |  | 
| 274 | 0 |  |  |  |  | 0 | return $class->new( bindings => \%data ); | 
| 275 | 0 |  |  |  |  | 0 | } | 
| 276 |  |  |  |  |  |  | } | 
| 277 | 0 |  |  |  |  | 0 |  | 
| 278 | 0 |  |  |  |  | 0 | use Encode qw(encode); | 
| 279 | 0 | 0 | 0 |  |  | 0 | use List::MoreUtils qw(zip); | 
| 280 | 0 |  |  |  |  | 0 | use Scalar::Util qw(blessed); | 
| 281 |  |  |  |  |  |  | use Attean::RDF; | 
| 282 | 0 |  |  |  |  | 0 | use Attean::API::Query; | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | use Moo::Role; | 
| 285 | 0 |  |  |  |  | 0 |  | 
| 286 |  |  |  |  |  |  | with 'Attean::API::SPARQLSerializable'; | 
| 287 |  |  |  |  |  |  |  | 
| 288 | 0 |  |  |  |  | 0 | around BUILDARGS => sub { | 
| 289 |  |  |  |  |  |  | my $orig 	= shift; | 
| 290 |  |  |  |  |  |  | my $class	= shift; | 
| 291 |  |  |  |  |  |  | my @args	= @_; | 
| 292 |  |  |  |  |  |  | if (scalar(@args) == 0 or not(defined($_[0])) or blessed($args[0])) { | 
| 293 | 50 |  |  | 50 |  | 68737 | my @names	= $class->variables; | 
|  | 50 |  |  |  |  | 148 |  | 
|  | 50 |  |  |  |  | 2705 |  | 
| 294 | 50 |  |  | 50 |  | 303 | foreach my $i (0 .. $#names) { | 
|  | 50 |  |  |  |  | 113 |  | 
|  | 50 |  |  |  |  | 269 |  | 
| 295 | 50 |  |  | 50 |  | 27283 | my $k	= $names[$i]; | 
|  | 50 |  |  |  |  | 102 |  | 
|  | 50 |  |  |  |  | 1893 |  | 
| 296 | 50 |  |  | 50 |  | 309 | my $v	= $args[$i]; | 
|  | 50 |  |  |  |  | 109 |  | 
|  | 50 |  |  |  |  | 484 |  | 
| 297 | 50 |  |  | 50 |  | 37036 | unless (defined($v)) { | 
|  | 50 |  |  |  |  | 108 |  | 
|  | 50 |  |  |  |  | 870 |  | 
| 298 |  |  |  |  |  |  | $args[$i]	= Attean::RDF::variable($k); | 
| 299 | 50 |  |  | 50 |  | 287 | } | 
|  | 50 |  |  |  |  | 88 |  | 
|  | 50 |  |  |  |  | 230 |  | 
| 300 |  |  |  |  |  |  | } | 
| 301 |  |  |  |  |  |  | my %args; | 
| 302 |  |  |  |  |  |  | @args{ $class->variables }	= @args; | 
| 303 |  |  |  |  |  |  | return $class->$orig(%args); | 
| 304 |  |  |  |  |  |  | } elsif (scalar(@args) == 2) { | 
| 305 |  |  |  |  |  |  | if (defined($args[0]) and $args[0] eq 'bindings') { | 
| 306 |  |  |  |  |  |  | return $class->$orig(%{ $args[1] }); | 
| 307 |  |  |  |  |  |  | } | 
| 308 |  |  |  |  |  |  | } | 
| 309 |  |  |  |  |  |  |  | 
| 310 |  |  |  |  |  |  | if (scalar(@_) % 2) { | 
| 311 |  |  |  |  |  |  | Carp::cluck; | 
| 312 |  |  |  |  |  |  | } | 
| 313 |  |  |  |  |  |  | my %args	= @_; | 
| 314 |  |  |  |  |  |  | foreach my $k ($class->variables) { | 
| 315 |  |  |  |  |  |  | if (not(exists $args{$k}) or not($args{$k})) { | 
| 316 |  |  |  |  |  |  | $args{$k}	= Attean::RDF::variable($k); | 
| 317 |  |  |  |  |  |  | } | 
| 318 |  |  |  |  |  |  | } | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | return $class->$orig(%args); | 
| 321 |  |  |  |  |  |  | }; | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | my $self	= shift; | 
| 324 |  |  |  |  |  |  | my $class	= ref($self); | 
| 325 |  |  |  |  |  |  | my $mapper	= shift; | 
| 326 |  |  |  |  |  |  | my %values; | 
| 327 |  |  |  |  |  |  | foreach my $pos ($self->variables) { | 
| 328 |  |  |  |  |  |  | my $value	= $self->value($pos); | 
| 329 |  |  |  |  |  |  | if ($value->does('Attean::API::Binding')) { | 
| 330 |  |  |  |  |  |  | $values{$pos}	= $value->apply_map($mapper); | 
| 331 |  |  |  |  |  |  | } else { | 
| 332 |  |  |  |  |  |  | $values{$pos}	= $mapper->map($value); | 
| 333 |  |  |  |  |  |  | } | 
| 334 |  |  |  |  |  |  | } | 
| 335 |  |  |  |  |  |  | return $class->new( %values ); | 
| 336 |  |  |  |  |  |  | } | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | my $self	= shift; | 
| 339 | 37 |  |  | 37 | 0 | 67 | my $class	= ref($self); | 
| 340 | 37 |  |  |  |  | 74 | my $bind	= shift; | 
| 341 | 37 |  |  |  |  | 70 | my %data; | 
| 342 | 37 |  |  |  |  | 68 | foreach my $k ($self->variables) { | 
| 343 | 37 |  |  |  |  | 106 | my $v	= $self->value($k); | 
| 344 | 139 |  |  |  |  | 1131 | if ($v->does('Attean::API::Variable')) { | 
| 345 | 139 | 50 |  |  |  | 394 | my $name	= $v->value; | 
| 346 | 0 |  |  |  |  | 0 | my $replace	= $bind->value($name); | 
| 347 |  |  |  |  |  |  | if (defined($replace) and blessed($replace)) { | 
| 348 | 139 |  |  |  |  | 2783 | $data{ $k }	= $replace; | 
| 349 |  |  |  |  |  |  | } else { | 
| 350 |  |  |  |  |  |  | $data{ $k }	= $v; | 
| 351 | 37 |  |  |  |  | 1011 | } | 
| 352 |  |  |  |  |  |  | } | 
| 353 |  |  |  |  |  |  | } | 
| 354 |  |  |  |  |  |  | return Attean::Result->new( bindings => \%data ); | 
| 355 | 2 |  |  | 2 | 0 | 4 | } | 
| 356 | 2 |  |  |  |  | 4 |  | 
| 357 | 2 |  |  |  |  | 5 | my $self	= shift; | 
| 358 | 2 |  |  |  |  | 3 | my $type	= ref($self); | 
| 359 | 2 |  |  |  |  | 6 | my $role	= $self->does('Attean::API::TriplePattern') ? 'Attean::API::TriplePattern' : 'Attean::API::QuadPattern'; | 
| 360 | 7 |  |  |  |  | 48 | my $iter	= Attean::ListIterator->new( values => [$self], item_type => $role ); | 
| 361 | 7 | 100 |  |  |  | 21 | my $triples	= $iter->canonical_set(); | 
| 362 | 3 |  |  |  |  | 48 | my ($t)		= @$triples; | 
| 363 | 3 |  |  |  |  | 7 | return $t; | 
| 364 | 3 | 50 | 33 |  |  | 18 | } | 
| 365 | 3 |  |  |  |  | 8 |  | 
| 366 |  |  |  |  |  |  | my $self	= shift; | 
| 367 | 0 |  |  |  |  | 0 | my $result	= shift; | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | my %bindings; | 
| 370 |  |  |  |  |  |  | my @vars	= $self->variables(); | 
| 371 | 2 |  |  |  |  | 71 | foreach my $pos (@vars) { | 
| 372 |  |  |  |  |  |  | my $pp	= $self->$pos(); | 
| 373 |  |  |  |  |  |  | if ($pp->does('Attean::API::Variable')) { | 
| 374 |  |  |  |  |  |  | $bindings{ $pos }	= $result->value($pp->value); | 
| 375 | 14 |  |  | 14 | 0 | 71 | } elsif ($pp->does('Attean::API::TriplePattern')) { | 
| 376 | 14 |  |  |  |  | 19 | my $sub_ground	= $pp->ground($result); | 
| 377 | 14 | 100 |  |  |  | 30 | $bindings{ $pos }	= $sub_ground; | 
| 378 | 14 |  |  |  |  | 415 | } else { | 
| 379 | 14 |  |  |  |  | 2502 | $bindings{ $pos }	= $pp; | 
| 380 | 14 |  |  |  |  | 15 | } | 
| 381 | 14 |  |  |  |  | 59 | } | 
| 382 |  |  |  |  |  |  |  | 
| 383 |  |  |  |  |  |  | return scalar(@vars) == 3 | 
| 384 |  |  |  |  |  |  | ? Attean::Triple->new( %bindings ) | 
| 385 | 85 |  |  | 85 | 0 | 155 | : Attean::Quad->new( %bindings ); | 
| 386 | 85 |  |  |  |  | 104 | } | 
| 387 |  |  |  |  |  |  |  | 
| 388 | 85 |  |  |  |  | 118 | my $self	= shift; | 
| 389 | 85 |  |  |  |  | 168 | my $quad	= shift; | 
| 390 | 85 |  |  |  |  | 155 | my %binding; | 
| 391 | 340 |  |  |  |  | 665 | foreach my $pos ($self->variables) { | 
| 392 | 340 | 100 |  |  |  | 631 | my $pp	= $self->$pos(); | 
|  |  | 50 |  |  |  |  |  | 
| 393 | 200 |  |  |  |  | 2467 | my $qp	= $quad->$pos(); | 
| 394 |  |  |  |  |  |  | if ($pp->does('Attean::API::Variable')) { | 
| 395 | 0 |  |  |  |  | 0 | if (my $already = $binding{ $pp->value }) { | 
| 396 | 0 |  |  |  |  | 0 | return unless $already->equals($qp); | 
| 397 |  |  |  |  |  |  | } | 
| 398 | 140 |  |  |  |  | 4667 | $binding{ $pp->value }	= $qp; | 
| 399 |  |  |  |  |  |  | } elsif ($pp->does('Attean::API::TriplePattern')) { | 
| 400 |  |  |  |  |  |  | return unless ($qp->does('Attean::API::Triple')); | 
| 401 |  |  |  |  |  |  | my $sub_binding	= $pp->unify($qp); | 
| 402 | 85 | 50 |  |  |  | 1690 | return unless $sub_binding; | 
| 403 |  |  |  |  |  |  | my $bkeys	= Set::Scalar->new(keys %binding); | 
| 404 |  |  |  |  |  |  | my $sbkeys	= Set::Scalar->new($sub_binding->variables); | 
| 405 |  |  |  |  |  |  | my $i		= $bkeys->intersection($sbkeys); | 
| 406 |  |  |  |  |  |  | for my $key ($i->elements) { | 
| 407 |  |  |  |  |  |  | # variable bound in multiple places with different values | 
| 408 | 316 |  |  | 316 | 0 | 438 | return unless ($binding{$key}->equals($sub_binding->value($key))); | 
| 409 | 316 |  |  |  |  | 360 | } | 
| 410 | 316 |  |  |  |  | 372 | my $mapping	= {$sub_binding->mapping}; | 
| 411 | 316 |  |  |  |  | 624 | @binding{ keys %$mapping }	= values %$mapping; | 
| 412 | 1221 |  |  |  |  | 2325 | } else { | 
| 413 | 1221 |  |  |  |  | 2044 | # bound position doesn't match | 
| 414 | 1221 | 100 |  |  |  | 2134 | use Data::Dumper; | 
|  |  | 50 |  |  |  |  |  | 
| 415 | 849 | 50 |  |  |  | 10020 | if ($pp->does('Attean::API::QuadPattern')) { | 
| 416 | 0 | 0 |  |  |  | 0 | Carp::cluck 'XXX unify: ' . Dumper($self); | 
| 417 |  |  |  |  |  |  | } | 
| 418 | 849 |  |  |  |  | 1670 |  | 
| 419 |  |  |  |  |  |  | return unless ($pp->equals($qp)); | 
| 420 | 0 | 0 |  |  |  | 0 | } | 
| 421 | 0 |  |  |  |  | 0 | } | 
| 422 | 0 | 0 |  |  |  | 0 |  | 
| 423 | 0 |  |  |  |  | 0 | # 		warn 'final mapping: ' . Dumper(\%binding); | 
| 424 | 0 |  |  |  |  | 0 | return Attean::Result->new( bindings => \%binding ); | 
| 425 | 0 |  |  |  |  | 0 | } | 
| 426 | 0 |  |  |  |  | 0 |  | 
| 427 |  |  |  |  |  |  | =item C<< parse ( $string ) >> | 
| 428 | 0 | 0 |  |  |  | 0 |  | 
| 429 |  |  |  |  |  |  | Returns a triple or quad pattern object using the variables and/or terms | 
| 430 | 0 |  |  |  |  | 0 | parsed from C<< $string >> in SPARQL syntax. | 
| 431 | 0 |  |  |  |  | 0 |  | 
| 432 |  |  |  |  |  |  | =cut | 
| 433 |  |  |  |  |  |  |  | 
| 434 | 50 |  |  | 50 |  | 59390 | my $self	= shift; | 
|  | 50 |  |  |  |  | 108 |  | 
|  | 50 |  |  |  |  | 16089 |  | 
| 435 | 372 | 50 |  |  |  | 11955 | my $class	= ref($self) || $self; | 
| 436 | 0 |  |  |  |  | 0 | my $string	= shift; | 
| 437 |  |  |  |  |  |  | my $bytes	= encode('UTF-8', $string, Encode::FB_CROAK); | 
| 438 |  |  |  |  |  |  | my $parser	= Attean->get_parser('SPARQL')->new(@_); | 
| 439 | 372 | 100 |  |  |  | 6297 | my @values	= $parser->parse_nodes($bytes); | 
| 440 |  |  |  |  |  |  | my @keys	= $self->variables; | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | my $f	= scalar(@values); | 
| 443 |  |  |  |  |  |  | my $e	= scalar(@keys); | 
| 444 | 301 |  |  |  |  | 6018 | unless ($e == $f) { | 
| 445 |  |  |  |  |  |  | die "${class}->parse found wrong number of nodes (found $f but expecting $e)"; | 
| 446 |  |  |  |  |  |  | } | 
| 447 |  |  |  |  |  |  | return $self->new(zip @keys, @values); | 
| 448 |  |  |  |  |  |  | } | 
| 449 |  |  |  |  |  |  | } | 
| 450 |  |  |  |  |  |  |  | 
| 451 |  |  |  |  |  |  | use List::MoreUtils qw(any); | 
| 452 |  |  |  |  |  |  | use Carp; | 
| 453 |  |  |  |  |  |  |  | 
| 454 |  |  |  |  |  |  | use Moo::Role; | 
| 455 | 4 |  |  | 4 | 0 | 20940 |  | 
| 456 | 4 |  | 33 |  |  | 25 | with 'Attean::API::TripleOrQuadPattern'; | 
| 457 | 4 |  |  |  |  | 8 |  | 
| 458 | 4 |  |  |  |  | 34 | my $self = shift; | 
| 459 | 4 |  |  |  |  | 415 | if (any { $_->does('Attean::API::Variable') } $self->values) { | 
| 460 | 4 |  |  |  |  | 241 | croak 'Use a Pattern class to construct when using variables'; | 
| 461 | 4 |  |  |  |  | 38 | } | 
| 462 |  |  |  |  |  |  | } | 
| 463 | 4 |  |  |  |  | 6 | } | 
| 464 | 4 |  |  |  |  | 9 |  | 
| 465 | 4 | 50 |  |  |  | 12 | use List::MoreUtils qw(zip); | 
| 466 | 0 |  |  |  |  | 0 | use Scalar::Util qw(blessed); | 
| 467 |  |  |  |  |  |  |  | 
| 468 | 4 |  |  |  |  | 419 | use Moo::Role; | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  |  | 
| 471 |  |  |  |  |  |  | my $self	= shift; | 
| 472 |  |  |  |  |  |  | my $key		= shift; | 
| 473 | 50 |  |  | 50 |  | 379 | return $self->$key() if ($key =~ /^(subject|predicate|object)$/); | 
|  | 50 |  |  |  |  | 119 |  | 
|  | 50 |  |  |  |  | 265 |  | 
| 474 | 50 |  |  | 50 |  | 44324 | die "Unrecognized binding name '$key'"; | 
|  | 50 |  |  |  |  | 106 |  | 
|  | 50 |  |  |  |  | 2291 |  | 
| 475 |  |  |  |  |  |  | } | 
| 476 | 50 |  |  | 50 |  | 260 |  | 
|  | 50 |  |  |  |  | 92 |  | 
|  | 50 |  |  |  |  | 216 |  | 
| 477 |  |  |  |  |  |  | my $self	= shift; | 
| 478 |  |  |  |  |  |  | my $graph	= shift; | 
| 479 |  |  |  |  |  |  | my @keys	= Attean::API::Quad->variables; | 
| 480 |  |  |  |  |  |  | my @values	= ($self->values, $graph); | 
| 481 | 673 |  |  | 673 | 0 | 23377 | return Attean::QuadPattern->new(zip @keys, @values); | 
| 482 | 673 | 100 |  | 2344 |  | 3457 | } | 
|  | 2344 |  |  |  |  | 34800 |  | 
| 483 | 2 |  |  |  |  | 54 |  | 
| 484 |  |  |  |  |  |  | my $self	= shift; | 
| 485 |  |  |  |  |  |  | unless ($self->is_ground) { | 
| 486 |  |  |  |  |  |  | die "Not a ground triple: " . $self->as_string; | 
| 487 |  |  |  |  |  |  | } | 
| 488 |  |  |  |  |  |  | my @terms	= map { $_->does('Attean::API::TriplePattern') ? $_->as_triple : $_ } $self->values; | 
| 489 | 50 |  |  | 50 |  | 19784 | return Attean::Triple->new(@terms); | 
|  | 50 |  |  |  |  | 134 |  | 
|  | 50 |  |  |  |  | 284 |  | 
| 490 | 50 |  |  | 50 |  | 26706 | } | 
|  | 50 |  |  |  |  | 125 |  | 
|  | 50 |  |  |  |  | 2323 |  | 
| 491 |  |  |  |  |  |  |  | 
| 492 | 50 |  |  | 50 |  | 313 | my $self	= shift; | 
|  | 50 |  |  |  |  | 128 |  | 
|  | 50 |  |  |  |  | 224 |  | 
| 493 |  |  |  |  |  |  | return $self->apply_statement(@_); | 
| 494 | 2739 |  |  | 2739 | 1 | 7167 | } | 
| 495 |  |  |  |  |  |  |  | 
| 496 |  |  |  |  |  |  | my $self	= shift; | 
| 497 | 4595 |  |  | 4595 | 1 | 4906 | my @tokens; | 
| 498 | 4595 |  |  |  |  | 4704 | foreach my $t ($self->values) { | 
| 499 | 4595 | 100 |  |  |  | 20752 | if ($t->does('Attean::API::TriplePattern')) { | 
| 500 | 1 |  |  |  |  | 11 | push(@tokens, AtteanX::SPARQL::Token->ltlt); | 
| 501 |  |  |  |  |  |  | push(@tokens, $t->sparql_tokens->elements); | 
| 502 |  |  |  |  |  |  | push(@tokens, AtteanX::SPARQL::Token->gtgt); | 
| 503 |  |  |  |  |  |  | } else { | 
| 504 | 26 |  |  | 26 | 1 | 94 | push(@tokens, $t->sparql_tokens->elements); | 
| 505 | 26 |  |  |  |  | 34 | } | 
| 506 | 26 |  |  |  |  | 82 | } | 
| 507 | 26 |  |  |  |  | 68 | return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' ); | 
| 508 | 26 |  |  |  |  | 548 | } | 
| 509 |  |  |  |  |  |  |  | 
| 510 |  |  |  |  |  |  | requires 'subject'; | 
| 511 |  |  |  |  |  |  | requires 'predicate'; | 
| 512 | 0 |  |  | 0 | 0 | 0 | requires 'object'; | 
| 513 | 0 | 0 |  |  |  | 0 |  | 
| 514 | 0 |  |  |  |  | 0 | with 'Attean::API::TripleOrQuadPattern', 'Attean::API::Binding', 'Attean::API::TermOrVariableOrTriplePattern'; | 
| 515 |  |  |  |  |  |  | } | 
| 516 | 0 | 0 |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 517 | 0 |  |  |  |  | 0 | use Scalar::Util qw(blessed); | 
| 518 |  |  |  |  |  |  | use Moo::Role; | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | if ($ENV{ATTEAN_TYPECHECK}) { | 
| 521 | 1 |  |  | 1 | 0 | 38 | my %map	= ( | 
| 522 | 1 |  |  |  |  | 5 | subject		=> 'Attean::API::BlankOrIRIOrTriple', | 
| 523 |  |  |  |  |  |  | predicate	=> 'Attean::API::IRI', | 
| 524 |  |  |  |  |  |  | object		=> 'Attean::API::TermOrTriple' | 
| 525 |  |  |  |  |  |  | ); | 
| 526 | 102 |  |  | 102 | 0 | 122 | foreach my $method (keys %map) { | 
| 527 | 102 |  |  |  |  | 124 | my $role	= $map{$method}; | 
| 528 | 102 |  |  |  |  | 176 | around $method => sub { | 
| 529 | 306 | 100 |  |  |  | 775 | my $orig	= shift; | 
| 530 | 1 |  |  |  |  | 17 | my $self	= shift; | 
| 531 | 1 |  |  |  |  | 36 | my $class	= ref($self); | 
| 532 | 1 |  |  |  |  | 5 | my $term	= $self->$orig(@_); | 
| 533 |  |  |  |  |  |  | my $type	= Type::Tiny::Role->new( role => $role ); | 
| 534 | 305 |  |  |  |  | 5838 | my $err		= $type->validate($term); | 
| 535 |  |  |  |  |  |  | if ($err) { | 
| 536 |  |  |  |  |  |  | die "${class}'s $method failed conformance check for role $role"; | 
| 537 | 102 |  |  |  |  | 1333 | } | 
| 538 |  |  |  |  |  |  | return $term; | 
| 539 |  |  |  |  |  |  | }; | 
| 540 |  |  |  |  |  |  | } | 
| 541 |  |  |  |  |  |  | } | 
| 542 |  |  |  |  |  |  |  | 
| 543 |  |  |  |  |  |  | my $self	= shift; | 
| 544 |  |  |  |  |  |  | my $graph	= shift; | 
| 545 |  |  |  |  |  |  | return Attean::Quad->new($self->values, $graph); | 
| 546 |  |  |  |  |  |  | } | 
| 547 |  |  |  |  |  |  |  | 
| 548 | 50 |  |  | 50 |  | 36010 | my $self	= shift; | 
|  | 50 |  |  |  |  | 128 |  | 
|  | 50 |  |  |  |  | 2035 |  | 
| 549 | 50 |  |  | 50 |  | 311 | my @values	= $self->values; | 
|  | 50 |  |  |  |  | 111 |  | 
|  | 50 |  |  |  |  | 209 |  | 
| 550 |  |  |  |  |  |  | return join(' ', '<<', (map { $_->ntriples_string } @values), '>>'); | 
| 551 |  |  |  |  |  |  | } | 
| 552 |  |  |  |  |  |  |  | 
| 553 |  |  |  |  |  |  | my ($a, $b)	= @_; | 
| 554 |  |  |  |  |  |  | return 1 unless blessed($b); | 
| 555 |  |  |  |  |  |  | if (not $b->does('Attean::API::Triple')) { | 
| 556 |  |  |  |  |  |  | # this is a type-error for equality testing, but special handling is needed in calling code for ORDER BY in which Triples sort last (after literals) | 
| 557 |  |  |  |  |  |  | die "TypeError: cannot compare an RDF-star triple and a non-triple"; | 
| 558 |  |  |  |  |  |  | } | 
| 559 |  |  |  |  |  |  |  | 
| 560 |  |  |  |  |  |  | foreach my $pos ($a->variables) { | 
| 561 |  |  |  |  |  |  | my $at	= $a->$pos(); | 
| 562 |  |  |  |  |  |  | my $bt	= $b->$pos(); | 
| 563 |  |  |  |  |  |  | my $c	= $at->compare($bt); | 
| 564 |  |  |  |  |  |  |  | 
| 565 |  |  |  |  |  |  | # If they are equal, continue. otherwise check if either term is an IRI. | 
| 566 |  |  |  |  |  |  | # This is because term equality is defined for IRIs, but < and > isn't. | 
| 567 |  |  |  |  |  |  | next unless ($c); | 
| 568 |  |  |  |  |  |  |  | 
| 569 |  |  |  |  |  |  | unless ($Attean::API::Binding::ALLOW_IRI_COMPARISON) { | 
| 570 |  |  |  |  |  |  | for ($at, $bt) { | 
| 571 |  |  |  |  |  |  | if ($_->does('Attean::API::IRI')) { | 
| 572 |  |  |  |  |  |  | # 					Carp::cluck "TypeError comparison of IRI " . $at->ntriples_string . " <=> " . $bt->ntriples_string . "\n"; | 
| 573 |  |  |  |  |  |  | # 					last; | 
| 574 |  |  |  |  |  |  | die "TypeError comparison of IRI" if ($_->does('Attean::API::IRI')); # comparison of IRIs is only defined for `ORDER BY`, not for general expressions | 
| 575 | 127 |  |  | 127 | 1 | 357 | } | 
| 576 | 127 |  |  |  |  | 206 | } | 
| 577 | 127 |  |  |  |  | 326 | } | 
| 578 |  |  |  |  |  |  |  | 
| 579 |  |  |  |  |  |  | if ($c) { | 
| 580 |  |  |  |  |  |  | return $c; | 
| 581 | 0 |  |  | 0 | 0 | 0 | } | 
| 582 | 0 |  |  |  |  | 0 | } | 
| 583 | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 584 |  |  |  |  |  |  | return 0; | 
| 585 |  |  |  |  |  |  | # 		return $a->ntriples_string cmp $b->ntriples_string; | 
| 586 |  |  |  |  |  |  | } | 
| 587 | 0 |  |  | 0 | 0 | 0 |  | 
| 588 | 0 | 0 |  |  |  | 0 | with 'Attean::API::TriplePattern', 'Attean::API::TripleOrQuad', 'Attean::API::Binding', 'Attean::API::TermOrVariableOrTriplePattern'; | 
| 589 | 0 | 0 |  |  |  | 0 | with 'Attean::API::BlankOrIRIOrTriple'; | 
| 590 |  |  |  |  |  |  | with 'Attean::API::TermOrTriple'; | 
| 591 | 0 |  |  |  |  | 0 | } | 
| 592 |  |  |  |  |  |  |  | 
| 593 |  |  |  |  |  |  | use Scalar::Util qw(blessed); | 
| 594 | 0 |  |  |  |  | 0 | use List::MoreUtils qw(zip); | 
| 595 | 0 |  |  |  |  | 0 |  | 
| 596 | 0 |  |  |  |  | 0 | use Moo::Role; | 
| 597 | 0 |  |  |  |  | 0 |  | 
| 598 |  |  |  |  |  |  | my $self	= shift; | 
| 599 |  |  |  |  |  |  | my $key		= shift; | 
| 600 |  |  |  |  |  |  | return $self->$key() if ($key =~ /^(subject|predicate|object|graph)$/); | 
| 601 | 0 | 0 |  |  |  | 0 | die "Unrecognized binding name '$key'"; | 
| 602 |  |  |  |  |  |  | } | 
| 603 | 0 | 0 |  |  |  | 0 |  | 
| 604 | 0 |  |  |  |  | 0 | my $self	= shift; | 
| 605 | 0 | 0 |  |  |  | 0 | unless ($self->is_ground) { | 
| 606 |  |  |  |  |  |  | die "Not a ground quad: " . $self->as_string; | 
| 607 |  |  |  |  |  |  | } | 
| 608 | 0 | 0 |  |  |  | 0 | return Attean::Quad->new($self->values); | 
| 609 |  |  |  |  |  |  | } | 
| 610 |  |  |  |  |  |  |  | 
| 611 |  |  |  |  |  |  | my $self	= shift; | 
| 612 |  |  |  |  |  |  | return $self->apply_statement(@_); | 
| 613 | 0 | 0 |  |  |  | 0 | } | 
| 614 | 0 |  |  |  |  | 0 |  | 
| 615 |  |  |  |  |  |  | my $self	= shift; | 
| 616 |  |  |  |  |  |  | my @keys	= Attean::API::Triple->variables; | 
| 617 |  |  |  |  |  |  | my @values	= $self->values; | 
| 618 | 0 |  |  |  |  | 0 | @values		= @values[0 .. scalar(@keys)-1]; | 
| 619 |  |  |  |  |  |  | return Attean::TriplePattern->new(zip @keys, @values); | 
| 620 |  |  |  |  |  |  | } | 
| 621 |  |  |  |  |  |  |  | 
| 622 |  |  |  |  |  |  | my $self	= shift; | 
| 623 |  |  |  |  |  |  | my @tokens; | 
| 624 |  |  |  |  |  |  | push(@tokens, AtteanX::SPARQL::Token->keyword('GRAPH')); | 
| 625 |  |  |  |  |  |  | push(@tokens, $self->graph->sparql_tokens->elements); | 
| 626 |  |  |  |  |  |  | push(@tokens, AtteanX::SPARQL::Token->lbrace()); | 
| 627 |  |  |  |  |  |  | my @values	= ($self->values)[0..2]; | 
| 628 | 50 |  |  | 50 |  | 35042 | foreach my $t (@values) { | 
|  | 50 |  |  |  |  | 130 |  | 
|  | 50 |  |  |  |  | 2299 |  | 
| 629 | 50 |  |  | 50 |  | 317 | push(@tokens, $t->sparql_tokens->elements); | 
|  | 50 |  |  |  |  | 116 |  | 
|  | 50 |  |  |  |  | 272 |  | 
| 630 |  |  |  |  |  |  | } | 
| 631 | 50 |  |  | 50 |  | 26966 | push(@tokens, AtteanX::SPARQL::Token->rbrace()); | 
|  | 50 |  |  |  |  | 99 |  | 
|  | 50 |  |  |  |  | 192 |  | 
| 632 |  |  |  |  |  |  | return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' ); | 
| 633 | 3018 |  |  | 3018 | 1 | 7723 | } | 
| 634 |  |  |  |  |  |  |  | 
| 635 | 5358 |  |  | 5358 | 1 | 7823 | requires 'subject'; | 
| 636 | 5358 |  |  |  |  | 5500 | requires 'predicate'; | 
| 637 | 5358 | 100 |  |  |  | 20368 | requires 'object'; | 
| 638 | 1 |  |  |  |  | 11 | requires 'graph'; | 
| 639 |  |  |  |  |  |  |  | 
| 640 |  |  |  |  |  |  | with 'Attean::API::TripleOrQuadPattern', 'Attean::API::Binding'; | 
| 641 |  |  |  |  |  |  | } | 
| 642 | 0 |  |  | 0 | 0 | 0 |  | 
| 643 | 0 | 0 |  |  |  | 0 | use Moo::Role; | 
| 644 | 0 |  |  |  |  | 0 |  | 
| 645 |  |  |  |  |  |  | if ($ENV{ATTEAN_TYPECHECK}) { | 
| 646 | 0 |  |  |  |  | 0 | my $type	= Type::Tiny::Role->new( role => 'Attean::API::BlankOrIRI' ); | 
| 647 |  |  |  |  |  |  | around 'graph' => sub { | 
| 648 |  |  |  |  |  |  | my $orig	= shift; | 
| 649 |  |  |  |  |  |  | my $self	= shift; | 
| 650 | 1 |  |  | 1 | 0 | 40 | my $class	= ref($self); | 
| 651 | 1 |  |  |  |  | 5 | my $term	= $self->$orig(@_); | 
| 652 |  |  |  |  |  |  | my $err		= $type->validate($term); | 
| 653 |  |  |  |  |  |  | die "${class}'s graph failed conformance check for role Attean::API::BlankOrIRI: $term" if ($err); | 
| 654 |  |  |  |  |  |  | return $term; | 
| 655 | 0 |  |  | 0 | 0 | 0 | }; | 
| 656 | 0 |  |  |  |  | 0 | } | 
| 657 | 0 |  |  |  |  | 0 |  | 
| 658 | 0 |  |  |  |  | 0 | my $self	= shift; | 
| 659 | 0 |  |  |  |  | 0 | my @values	= $self->values; | 
| 660 |  |  |  |  |  |  | return Attean::Triple->new(@values[0..2]); | 
| 661 |  |  |  |  |  |  | } | 
| 662 |  |  |  |  |  |  |  | 
| 663 | 2 |  |  | 2 | 0 | 70 | with 'Attean::API::QuadPattern'; | 
| 664 | 2 |  |  |  |  | 4 | with 'Attean::API::TripleOrQuad', 'Attean::API::TripleOrQuadPattern', 'Attean::API::Triple'; | 
| 665 | 2 |  |  |  |  | 8 | } | 
| 666 | 2 |  |  |  |  | 83 |  | 
| 667 | 2 |  |  |  |  | 9 |  | 
| 668 | 2 |  |  |  |  | 91 | use Scalar::Util qw(refaddr); | 
| 669 | 2 |  |  |  |  | 5 | use Types::Standard qw(HashRef); | 
| 670 | 6 |  |  |  |  | 16 |  | 
| 671 |  |  |  |  |  |  | use Moo::Role; | 
| 672 | 2 |  |  |  |  | 7 |  | 
| 673 | 2 |  |  |  |  | 93 | has 'eval_stash' => (is => 'rw', isa => HashRef); | 
| 674 |  |  |  |  |  |  |  | 
| 675 |  |  |  |  |  |  | my $self	= shift; | 
| 676 |  |  |  |  |  |  | if (not $self->eval_stash) { | 
| 677 |  |  |  |  |  |  | $self->eval_stash({}); | 
| 678 |  |  |  |  |  |  | } | 
| 679 |  |  |  |  |  |  | } | 
| 680 |  |  |  |  |  |  |  | 
| 681 |  |  |  |  |  |  | my $self	= shift; | 
| 682 |  |  |  |  |  |  | my $class	= ref($self); | 
| 683 |  |  |  |  |  |  | my $rowb	= shift; | 
| 684 |  |  |  |  |  |  |  | 
| 685 | 50 |  |  | 50 |  | 38413 | my %keysa; | 
|  | 50 |  |  |  |  | 122 |  | 
|  | 50 |  |  |  |  | 226 |  | 
| 686 |  |  |  |  |  |  | my @keysa	= $self->variables; | 
| 687 |  |  |  |  |  |  | @keysa{ @keysa }	= (1) x scalar(@keysa); | 
| 688 |  |  |  |  |  |  | my @shared	= grep { exists $keysa{ $_ } } ($rowb->variables); | 
| 689 |  |  |  |  |  |  | return @shared; | 
| 690 |  |  |  |  |  |  | } | 
| 691 |  |  |  |  |  |  |  | 
| 692 |  |  |  |  |  |  | my $self	= shift; | 
| 693 |  |  |  |  |  |  | my $class	= ref($self); | 
| 694 |  |  |  |  |  |  | my $rowb	= shift; | 
| 695 |  |  |  |  |  |  | my @shared	= $self->shared_domain($rowb); | 
| 696 |  |  |  |  |  |  | foreach my $key (@shared) { | 
| 697 |  |  |  |  |  |  | my $val_a	= $self->value($key); | 
| 698 |  |  |  |  |  |  | my $val_b	= $rowb->value($key); | 
| 699 |  |  |  |  |  |  | my $equal	= (refaddr($val_a) == refaddr($val_b)) || $val_a->equals( $val_b ); | 
| 700 |  |  |  |  |  |  | unless ($equal) { | 
| 701 | 15 |  |  | 15 | 0 | 24 | return; | 
| 702 | 15 |  |  |  |  | 26 | } | 
| 703 | 15 |  |  |  |  | 281 | } | 
| 704 |  |  |  |  |  |  |  | 
| 705 |  |  |  |  |  |  | my $row	= { (map { $_ => $self->value($_) } grep { defined($self->value($_)) } $self->variables), (map { $_ => $rowb->value($_) } grep { defined($rowb->value($_)) } $rowb->variables) }; | 
| 706 |  |  |  |  |  |  | my $joined	= Attean::Result->new( bindings => $row ); | 
| 707 |  |  |  |  |  |  | return $joined; | 
| 708 |  |  |  |  |  |  | } | 
| 709 |  |  |  |  |  |  |  | 
| 710 |  |  |  |  |  |  | =item C<< project( @keys ) >> | 
| 711 |  |  |  |  |  |  |  | 
| 712 | 50 |  |  | 50 |  | 24586 | Returns a new L<Attean::Result> binding which contains variable-value mappings | 
|  | 50 |  |  |  |  | 105 |  | 
|  | 50 |  |  |  |  | 2435 |  | 
| 713 | 50 |  |  | 50 |  | 333 | from the invocant for every variable name in C<< @keys >>. | 
|  | 50 |  |  |  |  | 219 |  | 
|  | 50 |  |  |  |  | 462 |  | 
| 714 |  |  |  |  |  |  |  | 
| 715 | 50 |  |  | 50 |  | 27123 | =cut | 
|  | 50 |  |  |  |  | 108 |  | 
|  | 50 |  |  |  |  | 192 |  | 
| 716 |  |  |  |  |  |  |  | 
| 717 |  |  |  |  |  |  | my $self	= shift; | 
| 718 |  |  |  |  |  |  | my @vars	= @_; | 
| 719 |  |  |  |  |  |  | my %bindings; | 
| 720 | 586 |  |  | 586 | 0 | 126866 | foreach my $v (@vars) { | 
| 721 | 586 | 50 |  |  |  | 8237 | my $term	= $self->value($v); | 
| 722 | 586 |  |  |  |  | 10402 | $bindings{ $v }	= $term if ($term); | 
| 723 |  |  |  |  |  |  | } | 
| 724 |  |  |  |  |  |  | return Attean::Result->new( bindings => \%bindings ); | 
| 725 |  |  |  |  |  |  | } | 
| 726 |  |  |  |  |  |  |  | 
| 727 | 35 |  |  | 35 | 0 | 53 | my $self	= shift; | 
| 728 | 35 |  |  |  |  | 51 | my %vars	= map { $_ => 1 } @_; | 
| 729 | 35 |  |  |  |  | 55 | my %bindings; | 
| 730 |  |  |  |  |  |  | foreach my $v ($self->variables) { | 
| 731 | 35 |  |  |  |  | 50 | unless ($vars{$v}) { | 
| 732 | 35 |  |  |  |  | 98 | my $term	= $self->value($v); | 
| 733 | 35 |  |  |  |  | 113 | $bindings{ $v }	= $term; | 
| 734 | 35 |  |  |  |  | 97 | } | 
|  | 55 |  |  |  |  | 141 |  | 
| 735 | 35 |  |  |  |  | 85 | } | 
| 736 |  |  |  |  |  |  | return Attean::Result->new( bindings => \%bindings ); | 
| 737 |  |  |  |  |  |  | } | 
| 738 |  |  |  |  |  |  |  | 
| 739 | 33 |  |  | 33 | 1 | 275 | my $self	= shift; | 
| 740 | 33 |  |  |  |  | 57 | my $class	= ref($self); | 
| 741 | 33 |  |  |  |  | 58 | my $mapper	= shift; | 
| 742 | 33 |  |  |  |  | 70 | my %values; | 
| 743 | 33 |  |  |  |  | 108 | foreach my $var ($self->variables) { | 
| 744 | 9 |  |  |  |  | 28 | my $value	= $self->value($var); | 
| 745 | 9 |  |  |  |  | 23 | if ($value->does('Attean::API::Binding')) { | 
| 746 | 9 |  | 100 |  |  | 77 | $values{$var}	= $value->apply_map($mapper); | 
| 747 | 9 | 100 |  |  |  | 76 | } else { | 
| 748 | 3 |  |  |  |  | 20 | my $term	= $mapper->map($value); | 
| 749 |  |  |  |  |  |  | if ($term) { | 
| 750 |  |  |  |  |  |  | $values{$var}	= $term; | 
| 751 |  |  |  |  |  |  | } | 
| 752 | 30 |  |  |  |  | 84 | } | 
|  | 39 |  |  |  |  | 101 |  | 
|  | 39 |  |  |  |  | 92 |  | 
|  | 47 |  |  |  |  | 127 |  | 
|  | 47 |  |  |  |  | 86 |  | 
| 753 | 30 |  |  |  |  | 605 | } | 
| 754 | 30 |  |  |  |  | 746 | return $class->new( bindings => \%values ); | 
| 755 |  |  |  |  |  |  | } | 
| 756 |  |  |  |  |  |  |  | 
| 757 |  |  |  |  |  |  | with 'Attean::API::Binding', 'Attean::API::ResultOrTerm'; | 
| 758 |  |  |  |  |  |  | } | 
| 759 |  |  |  |  |  |  |  | 
| 760 |  |  |  |  |  |  | 1; | 
| 761 |  |  |  |  |  |  |  | 
| 762 |  |  |  |  |  |  |  | 
| 763 |  |  |  |  |  |  | =back | 
| 764 |  |  |  |  |  |  |  | 
| 765 | 86 |  |  | 86 | 0 | 146 | =head1 BUGS | 
| 766 | 86 |  |  |  |  | 177 |  | 
| 767 | 86 |  |  |  |  | 104 | Please report any bugs or feature requests to through the GitHub web interface | 
| 768 | 86 |  |  |  |  | 143 | at L<https://github.com/kasei/attean/issues>. | 
| 769 | 190 |  |  |  |  | 361 |  | 
| 770 | 190 | 50 |  |  |  | 467 | =head1 SEE ALSO | 
| 771 |  |  |  |  |  |  |  | 
| 772 | 86 |  |  |  |  | 1640 |  | 
| 773 |  |  |  |  |  |  |  | 
| 774 |  |  |  |  |  |  | =head1 AUTHOR | 
| 775 |  |  |  |  |  |  |  | 
| 776 | 61 |  |  | 61 | 0 | 88 | Gregory Todd Williams  C<< <gwilliams@cpan.org> >> | 
| 777 | 61 |  |  |  |  | 110 |  | 
|  | 4 |  |  |  |  | 15 |  | 
| 778 | 61 |  |  |  |  | 77 | =head1 COPYRIGHT | 
| 779 | 61 |  |  |  |  | 174 |  | 
| 780 | 161 | 100 |  |  |  | 281 | Copyright (c) 2014--2022 Gregory Todd Williams. | 
| 781 | 158 |  |  |  |  | 258 | This program is free software; you can redistribute it and/or modify it under | 
| 782 | 158 |  |  |  |  | 283 | the same terms as Perl itself. | 
| 783 |  |  |  |  |  |  |  | 
| 784 |  |  |  |  |  |  | =cut |