PageRenderTime 11ms CodeModel.GetById 3ms app.highlight 201ms RepoModel.GetById 2ms app.codeStats 0ms

/lib/RDF/Closure/Engine/OWL2RL.pm

https://bitbucket.org/tobyink/p5-rdf-closure
Perl | 1421 lines | 1226 code | 135 blank | 60 comment | 62 complexity | be10105b34086de3e1bc48c9993b4b64 MD5 | raw file
   1package RDF::Closure::Engine::OWL2RL;
   2
   3use 5.008;
   4use strict;
   5use utf8;
   6
   7use Error qw[:try];
   8use RDF::Trine qw[statement iri];
   9use RDF::Trine::Namespace qw[RDF RDFS OWL XSD];
  10use RDF::Closure::AxiomaticTriples qw[
  11	$OWLRL_Datatypes_Disjointness
  12	$OWLRL_Axiomatic_Triples
  13	$OWLRL_D_Axiomatic_Triples
  14	];
  15use RDF::Closure::DatatypeHandling qw[
  16	literals_identical
  17	literal_valid
  18	];
  19use RDF::Closure::XsdDatatypes qw[
  20	$OWL_RL_Datatypes
  21	$OWL_Datatype_Subsumptions
  22	];
  23use RDF::Closure::Rule::Programmatic;
  24use RDF::Closure::Rule::StatementMatcher;
  25use Scalar::Util qw[blessed];
  26
  27use constant {
  28	TRUE    => 1,
  29	FALSE   => 0,
  30	};
  31use namespace::clean;
  32
  33use base qw[RDF::Closure::Engine::Core];
  34
  35our $VERSION = '0.000_05';
  36
  37our @OneTimeRules = (
  38
  39	# dt-type2, dt-not-type, dt-diff, dt-eq
  40	RDF::Closure::Rule::Programmatic->new(
  41		sub {
  42				my ($cl, $rule) = @_;
  43				
  44				my $implicit = {};
  45				my $explicit = {};
  46				my $used_datatypes = {};
  47				
  48				local *_add_to_explicit = sub
  49				{
  50					my ($s, $o) = map { $_->sse } @_;
  51					$explicit->{$s} = {}
  52						unless exists $explicit->{$s};
  53					$explicit->{$s}{$o}++;
  54				};
  55
  56				local *_append_to_explicit = sub
  57				{
  58					my ($s, $o) = map { $_->sse } @_;
  59					$explicit->{$s} = {}
  60						unless exists $explicit->{$s};
  61					for my $d (keys %{ $explicit->{$o} })
  62					{
  63						$explicit->{$s}{$d}++;
  64					}
  65				};
  66				
  67				local *_add_to_used_datatypes = sub
  68				{
  69					my ($d) = @_;
  70					$d = $d->uri if blessed($d);
  71					$used_datatypes->{$d}++;
  72				};
  73				
  74				local *_handle_subsumptions = sub
  75				{
  76					my ($r, $dt) = @_;
  77					if (exists $OWL_Datatype_Subsumptions->{$dt})
  78					{
  79						foreach my $new_dt (@{ $OWL_Datatype_Subsumptions->{$dt} })
  80						{
  81							$cl->store_triple($r, $RDF->type, $new_dt);
  82							$cl->store_triple($new_dt, $RDF->type, $RDFS->Datatype);
  83							_add_to_used_datatypes($new_dt);
  84						}
  85					}
  86				};
  87				
  88				my %literals;
  89				$cl->graph->get_statements(undef, undef, undef)->each(sub
  90				{
  91					my $st    = shift;
  92					my @nodes = $st->nodes;
  93					foreach my $lt (@nodes)
  94					{
  95						next unless $lt->is_literal;
  96						# We're now effectively in a foreach literal loop...
  97						
  98						# Add to %literals, but skip rest of this iteration if it was already there.
  99						next if $literals{ $lt->sse };
 100						$literals{ $lt->sse } = $lt;
 101						
 102						next unless $lt->has_datatype;
 103						$cl->store_triple($lt, $RDF->type, iri($lt->literal_datatype));
 104						
 105						next unless grep { $_->uri eq $lt->literal_datatype } @$OWL_RL_Datatypes;
 106						
 107						# RULE dt-type2
 108						$implicit->{ $lt->sse } = $lt->literal_datatype
 109							unless exists $implicit->{ $lt->sse };
 110						_add_to_used_datatypes($lt->literal_datatype);
 111						
 112						# RULE dt-not-type
 113						$cl->add_error("Literal's lexical value and datatype do not match: (%s,%s)",
 114							$lt->literal_value, $lt->literal_datatype)
 115							unless $cl->dt_handling->literal_valid($lt);
 116					}
 117				});
 118				
 119				# RULE dt-diff
 120				# RULE dt-eq
 121				foreach my $lt1 (keys %literals)
 122				{
 123					foreach my $lt2 (keys %literals)
 124					{
 125						if ($lt1 ne $lt2) # @@TODO doesn't work ???
 126						{
 127							my $l1 = $literals{$lt1};
 128							my $l2 = $literals{$lt2};
 129							
 130							if ($cl->dt_handling->literals_identical($l1, $l2))
 131							{
 132								$cl->store_triple($l1, $OWL->sameAs, $l2);
 133							}
 134							else
 135							{
 136								$cl->store_triple($l1, $OWL->differentFrom, $l2);
 137							}
 138						}
 139					}
 140				}
 141
 142				# this next bit catches triples like { [] a xsd:string . }
 143				$cl->graph->get_statements(undef, $RDF->type, undef)->each(sub {
 144					my $st = shift;
 145					my ($s, $p, $o) = ($st->subject, $st->predicate, $st->object);
 146					if (grep { $_->equal($o); } @$OWL_RL_Datatypes)
 147					{
 148						_add_to_used_datatypes($o);
 149						_add_to_explicit($s, $o)
 150							unless exists $explicit->{ $s->sse };
 151					}
 152				});
 153
 154				$cl->graph->get_statements(undef, $OWL->sameAs, undef)->each(sub {
 155					my $st = shift;
 156					my ($s, $p, $o) = ($st->subject, $st->predicate, $st->object);
 157					_append_to_explicit($s, $o) if exists $explicit->{$o};
 158					_append_to_explicit($o, $s) if exists $explicit->{$s};
 159				});
 160				
 161				foreach my $dt (@$OWL_RL_Datatypes)
 162				{
 163					$cl->store_triple($dt, $RDF->type, $RDFS->Datatype);
 164				}
 165				foreach my $dts (values %$explicit)
 166				{
 167					foreach my $dt (keys %$dts)
 168					{
 169						$cl->store_triple(iri($dt), $RDF->type, $RDFS->Datatype);
 170					}
 171				}
 172				
 173				foreach my $r (keys %$explicit)
 174				{
 175					my @dtypes = keys %{ $explicit->{$r} };
 176					$r = RDF::Trine::Node->from_sse($r);
 177					foreach my $dt (@dtypes)
 178					{
 179						$dt = $1 if $dt =~ /^<(.+)>$/;
 180						_handle_subsumptions($r, $dt);
 181					}
 182				}
 183				
 184				foreach my $r (keys %$implicit)
 185				{
 186					my $dt = $implicit->{$r};
 187					$r = RDF::Trine::Node->from_sse($r);
 188					_handle_subsumptions($r, $dt);
 189				}
 190				
 191				foreach my $t (@$OWLRL_Datatypes_Disjointness)
 192				{
 193					my ($l, $r) = ($t->subject, $t->object);
 194					$cl->store_triple($t)
 195						if exists $used_datatypes->{$l->uri}
 196						&& exists $used_datatypes->{$r->uri};
 197				}
 198			},
 199		'dt-type2, dt-not-type, dt-diff, dt-eq'
 200		),
 201
 202	# cls-thing
 203	RDF::Closure::Rule::Programmatic->new(
 204		sub {
 205				my ($cl, $rule) = @_;
 206				$cl->store_triple($OWL->Thing, $RDF->type, $OWL->Class);
 207			},
 208		'cls-thing'
 209		),
 210
 211	# cls-nothing
 212	RDF::Closure::Rule::Programmatic->new(
 213		sub {
 214				my ($cl, $rule) = @_;
 215				$cl->store_triple($OWL->Nothing, $RDF->type, $OWL->Class);
 216			},
 217		'cls-nothing'
 218		),
 219
 220	# prp-ap
 221	RDF::Closure::Rule::Programmatic->new(
 222		sub {
 223				my ($cl, $rule) = @_;
 224				
 225				my $OWLRL_Annotation_properties = [
 226					$RDFS->label,
 227					$RDFS->comment,
 228					$RDFS->seeAlso,
 229					$RDFS->isDefinedBy,
 230					$OWL->deprecated,
 231					$OWL->versionInfo,
 232					$OWL->priorVersion,
 233					$OWL->backwardCompatibleWith,
 234					$OWL->incompatibleWith,
 235					];
 236				
 237				$cl->store_triple($_, $RDF->type, $OWL->AnnotationProperty)
 238					foreach @$OWLRL_Annotation_properties;
 239			},
 240		'prp-ap'
 241		),
 242
 243	);
 244
 245my $_EQ_REF = {};
 246
 247our @Rules = (
 248
 249	# prp-dom
 250	RDF::Closure::Rule::StatementMatcher->new(
 251		[undef, $RDFS->domain, undef],
 252		sub {
 253				my ($cl, $st, $rule) = @_; my ($prop, undef, $class) = $st->nodes;
 254				$cl->graph->subjects($prop)->each(sub {
 255					$cl->store_triple(shift, $RDF->type, $class);
 256				});
 257			},
 258		'prp-dom' # Same as rdfs2
 259		),
 260		
 261	# prp-rng
 262	RDF::Closure::Rule::StatementMatcher->new(
 263		[undef, $RDFS->range, undef],
 264		sub {
 265				my ($cl, $st, $rule) = @_; my ($prop, undef, $class) = $st->nodes;
 266				$cl->graph->objects(undef, $prop)->each(sub {
 267					$cl->store_triple(shift, $RDF->type, $class);
 268				});
 269			},
 270		'prp-rng' # Same as rdfs3
 271		),
 272	
 273	# prp-fp
 274	RDF::Closure::Rule::StatementMatcher->new(
 275		[undef, $RDF->type, $OWL->FunctionalProperty],
 276		sub {
 277				my ($cl, $st, $rule) = @_; my ($prop) = $st->nodes;
 278				$cl->graph->get_statements(undef, $prop, undef)->each(sub {
 279					my $x  = $st->subject;
 280					my $y1 = $st->object;
 281					$cl->graph->objects($x, $prop)->each(sub{
 282						my $y2 = shift;
 283						$cl->store_triple($y1, $OWL->sameAs, $y2)
 284							unless $y1->equal($y2);
 285					});
 286				});
 287			},
 288		'prp-fp'
 289		),
 290
 291	# prp-ifp
 292	RDF::Closure::Rule::StatementMatcher->new(
 293		[undef, $RDF->type, $OWL->InverseFunctionalProperty],
 294		sub {
 295				my ($cl, $st, $rule) = @_; my ($prop) = $st->nodes;
 296				$cl->graph->get_statements(undef, $prop, undef)->each(sub {
 297					my $st = shift;
 298					my $x  = $st->object;
 299					my $y1 = $st->subject;
 300					$cl->graph->subjects($prop, $x)->each(sub{
 301						my $y2 = shift;
 302						$cl->store_triple($y1, $OWL->sameAs, $y2)
 303							unless $y1->equal($y2);
 304					});
 305				});
 306			},
 307		'prp-ifp'
 308		),
 309
 310	# prp-irp
 311	RDF::Closure::Rule::StatementMatcher->new(
 312		[undef, $RDF->type, $OWL->IrreflexiveProperty],
 313		sub {
 314				my ($cl, $st, $rule) = @_; my ($prop) = $st->nodes;
 315				$cl->graph->get_statements(undef, $prop, undef)->each(sub{
 316					my $st = shift;
 317					$cl->add_error("Irreflexive property %s used reflexively on %s", $st->predicate, $st->subject)
 318						if $st->subject->equal($st->object);
 319				});
 320			},
 321		'prp-irp'
 322		),
 323
 324	# prp-symp
 325	RDF::Closure::Rule::StatementMatcher->new(
 326		[undef, $RDF->type, $OWL->SymmetricProperty],
 327		sub {
 328				my ($cl, $st, $rule) = @_; my ($prop) = $st->nodes;
 329				$cl->graph->get_statements(undef, $prop, undef)->each(sub{
 330					my $st = shift;
 331					$cl->store_triple($st->object, $prop, $st->subject);
 332				});
 333			},
 334		'prp-symp'
 335		),
 336
 337	# prp-asym
 338	RDF::Closure::Rule::StatementMatcher->new(
 339		[undef, $RDF->type, $OWL->AsymmetricProperty],
 340		sub {
 341				my ($cl, $st, $rule) = @_; my ($prop) = $st->nodes;
 342				$cl->graph->get_statements(undef, $prop, undef)->each(sub{
 343					my $st = shift;
 344					$cl->add_error("Asymmetric property %s used symmetrically on (%s,%s)", $st->predicate, $st->subject, $st->object)
 345						if $cl->graph->count_statements($st->object, $st->predicate, $st->subject);
 346				});
 347			},
 348		'prp-asym'
 349		),
 350
 351	# prp-trp
 352	RDF::Closure::Rule::StatementMatcher->new(
 353		[undef, $RDF->type, $OWL->TransitiveProperty],
 354		sub {
 355				my ($cl, $st, $rule) = @_; my ($prop) = $st->nodes;
 356				$cl->graph->get_statements(undef, $prop, undef)->each(sub{
 357					my ($x, undef, $y) = $_[0]->nodes;
 358					$cl->graph->objects($y, $prop)->each(sub{
 359						my $z = $_[0];
 360						$cl->store_triple($x, $prop, $z);
 361					});
 362				});
 363			},
 364		'prp-trp'
 365		),
 366
 367	# prp-adp
 368	RDF::Closure::Rule::StatementMatcher->new(
 369		[undef, $RDF->type, $OWL->AllDisjointProperties],
 370		sub {
 371				my ($cl, $st, $rule) = @_; my ($x) = $st->nodes;
 372				$cl->graph->get_statements($x, $OWL->members, undef)->each(sub {
 373					my @pis = $cl->graph->get_list($_[0]->object);
 374					for my $i (0 .. scalar(@pis)-1)
 375					{
 376						for my $j ($i+1 .. scalar(@pis)-1)
 377						{
 378							my $pi = $pis[$i];
 379							my $pj = $pis[$j];
 380							
 381							$cl->graph->get_statements(undef, $pi, undef)->each(sub {
 382								my ($x, undef, $y) = $_[0]->nodes;
 383								if ($cl->graph->count_statements($x, $pj, $y))
 384								{
 385									$cl->add_error("Disjoint properties in an 'AllDisjointProperties' are not really disjoint: %s %s %s and %s %s %s.", $x, $pi, $y, $x, $pj, $y);
 386								}
 387							});
 388						}						
 389					}
 390				});
 391			},
 392		'prp-adp'
 393		),
 394
 395	# prp-spo1
 396	RDF::Closure::Rule::StatementMatcher->new(
 397		[undef, $RDFS->subPropertyOf, undef],
 398		sub {
 399				my ($cl, $st, $rule) = @_; my ($prop1, undef, $prop2) = $st->nodes;
 400				$cl->graph->get_statements(undef, $prop1, undef)->each(sub {
 401					my $st = shift;
 402					$cl->store_triple($st->subject, $prop2, $st->object);
 403				});
 404			},
 405		'prp-spo1' # Same as rdfs7
 406		),
 407	
 408	# prp-spo2
 409	RDF::Closure::Rule::StatementMatcher->new(
 410		[undef, $OWL->propertyChainAxiom],
 411		sub {
 412				my ($cl, $st, $rule) = @_; my ($prop, undef, $chain) = $st->nodes;
 413				_property_chain($cl, $prop, $chain);
 414			},
 415		'prp-spo2'
 416		),
 417
 418	# prp-eqp1, prp-eqp2
 419	RDF::Closure::Rule::StatementMatcher->new(
 420		[undef, $OWL->equivalentProperty, undef],
 421		sub {
 422				my ($cl, $st, $rule) = @_; my ($prop1, undef, $prop2) = $st->nodes;
 423				return if $prop1->equal($prop2);
 424				$cl->graph->get_statements(undef, $prop1, undef)->each(sub {
 425					my $st = shift;
 426					$cl->store_triple($st->subject, $prop2, $st->object);
 427				});
 428				$cl->graph->get_statements(undef, $prop2, undef)->each(sub {
 429					my $st = shift;
 430					$cl->store_triple($st->subject, $prop1, $st->object);
 431				});
 432			},
 433		'prp-eqp1, prp-eqp2'
 434		),
 435
 436	# prp-pdw
 437	RDF::Closure::Rule::StatementMatcher->new(
 438		[undef, $OWL->propertyDisjointWith, undef],
 439		sub {
 440				my ($cl, $st, $rule) = @_; my ($prop1, undef, $prop2) = $st->nodes;
 441				$cl->graph->get_statements(undef, $prop1, undef)->each(sub {
 442					my $st = shift;
 443					$cl->add_error('Erronous usage of disjoint properties %s and %s on %s and %s', $prop1, $prop2, $st->subject, $st->object)
 444						if $cl->graph->count_statements($st->subject, $prop2, $st->object);
 445				});
 446			},
 447		'prp-pdw'
 448		),
 449
 450	# prp-inv1, prp-inv2
 451	RDF::Closure::Rule::StatementMatcher->new(
 452		[undef, $OWL->inverseOf, undef],
 453		sub {
 454				my ($cl, $st, $rule) = @_; my ($prop1, undef, $prop2) = $st->nodes;
 455				$cl->graph->get_statements(undef, $prop1, undef)->each(sub {
 456					my $st = shift;
 457					$cl->store_triple($st->object, $prop2, $st->subject);
 458				});
 459				return if $prop1->equal($prop2);
 460				$cl->graph->get_statements(undef, $prop2, undef)->each(sub {
 461					my $st = shift;
 462					$cl->store_triple($st->object, $prop1, $st->subject);
 463				});
 464			},
 465		'prp-inv1, prp-inv2'
 466		),
 467
 468	# prp-key
 469	RDF::Closure::Rule::StatementMatcher->new(
 470		[undef, $OWL->hasKey, undef],
 471		sub {
 472				my ($cl, $st, $rule) = @_; my ($c, $t, $u) = $st->nodes;
 473				my $G   = $cl->graph;
 474				my @pis = $G->get_list($u);
 475				if (@pis)
 476				{
 477					foreach my $x ($G->subjects($RDF->type, $c))
 478					{
 479						my $finalList = [ map { [$_] } $G->objects($x, $pis[0]) ];
 480						my (undef, @otherPIS) = @pis;
 481						foreach my $pi (@otherPIS)
 482						{
 483							my $newList = [];
 484							foreach my $zi ($G->objects($x, $pi))
 485							{
 486								foreach my $l (@$finalList)
 487								{
 488									push @$newList, [@$l, $zi];
 489								}
 490							}
 491							$finalList = $newList;
 492						}
 493						
 494						my $valueList = [ grep { scalar(@$_)==scalar(@pis) } @$finalList ];
 495						
 496						#use Data::Dumper;
 497						#printf("%s is member of class %s, has key values:\n%s\n",
 498						#	$x->as_ntriples,
 499						#	$c->as_ntriples,
 500						#	Dumper($valueList));
 501					
 502						INDY: foreach my $y ($G->subjects($RDF->type, $c))
 503						{
 504							next if $x->equal($y);
 505							next if $G->count_statements($x, $OWL->sameAs, $y);
 506							next if $G->count_statements($y, $OWL->sameAs, $x);
 507							
 508							foreach my $vals (@$valueList)
 509							{
 510								my $same = TRUE;
 511								PROP: for my $i (0 .. scalar(@pis)-1)
 512								{
 513									unless ($G->count_statements($y, $pis[$i], $vals->[$i]))
 514									{
 515										$same = FALSE;
 516										next PROP;
 517									}
 518								}
 519								
 520								if ($same)
 521								{
 522									$cl->store_triple($x, $OWL->sameAs, $y);
 523									$cl->store_triple($y, $OWL->sameAs, $x);
 524									next INDY;
 525								}
 526							}
 527						}
 528					}					
 529				}
 530			},
 531		'prp-key'
 532		),
 533
 534	# prp-npa1
 535	RDF::Closure::Rule::StatementMatcher->new(
 536		[undef, $OWL->targetIndividual, undef],
 537		sub {
 538				my ($cl, $st, $rule) = @_; my ($x, undef, $target) = $st->nodes;
 539				my @sources = $cl->graph->objects($x, $OWL->sourceIndividual);
 540				my @props   = $cl->graph->objects($x, $OWL->assertionProperty);
 541				foreach my $s (@sources)
 542				{
 543					foreach my $p (@props)
 544					{
 545						if ($cl->graph->count_statements($s, $p, $target))
 546						{
 547							$cl->add_error('Negative (object) property assertion violated for: (%s %s %s .)', $s, $p, $target);
 548						}
 549					}
 550				}
 551			},
 552		'prp-npa1'
 553		),
 554
 555	# prp-npa2
 556	RDF::Closure::Rule::StatementMatcher->new(
 557		[undef, $OWL->targetValue, undef],
 558		sub {
 559				my ($cl, $st, $rule) = @_; my ($x, undef, $target) = $st->nodes;
 560				my @sources = $cl->graph->objects($x, $OWL->sourceIndividual);
 561				my @props   = $cl->graph->objects($x, $OWL->assertionProperty);
 562				foreach my $s (@sources)
 563				{
 564					foreach my $p (@props)
 565					{
 566						if ($cl->graph->count_statements($s, $p, $target))
 567						{
 568							$cl->add_error('Negative (datatype) property assertion violated for: (%s %s %s .)', $s, $p, $target);
 569						}
 570					}
 571				}
 572			},
 573		'prp-npa2'
 574		),
 575
 576	# eq-ref
 577	RDF::Closure::Rule::StatementMatcher->new(
 578		[],
 579		sub {
 580				my ($cl, $st, $rule) = @_;
 581				my @nodes = $st->nodes;
 582				for (0..2)
 583				{
 584					next if $_EQ_REF->{ $nodes[$_]->sse }++; # optimisation
 585					$cl->store_triple($nodes[$_], $OWL->sameAs, $nodes[$_]);
 586				}
 587			},
 588		'eq-ref'
 589		),
 590
 591	# eq-sym
 592	RDF::Closure::Rule::StatementMatcher->new(
 593		[undef, $OWL->sameAs, undef],
 594		sub {
 595				my ($cl, $st, $rule) = @_; my ($s, $p, $o) = $st->nodes;
 596				$cl->store_triple($o, $OWL->sameAs, $s);
 597			},
 598		'eq-sym'
 599		),
 600
 601	# eq-trans
 602	RDF::Closure::Rule::StatementMatcher->new(
 603		[undef, $OWL->sameAs, undef],
 604		sub {
 605				my ($cl, $st, $rule) = @_; my ($s, $p, $o) = $st->nodes;
 606				foreach my $z ($cl->graph->objects($o, $OWL->sameAs))
 607				{
 608					$cl->store_triple($s, $OWL->sameAs, $z);
 609					$cl->store_triple($z, $OWL->sameAs, $s);
 610				}
 611			},
 612		'eq-trans'
 613		),
 614
 615	# eq-rep-s
 616	RDF::Closure::Rule::StatementMatcher->new(
 617		[undef, $OWL->sameAs, undef],
 618		sub {
 619				my ($cl, $st, $rule) = @_; my ($s, $p, $o) = $st->nodes;
 620				$cl->graph->get_statements($s, undef, undef)->each(sub {
 621					$cl->store_triple($o, $_[0]->predicate, $_[0]->object);
 622				});
 623			},
 624		'eq-rep-s'
 625		),
 626
 627	# eq-rep-p
 628	RDF::Closure::Rule::StatementMatcher->new(
 629		[undef, $OWL->sameAs, undef],
 630		sub {
 631				my ($cl, $st, $rule) = @_; my ($s, $p, $o) = $st->nodes;
 632				$cl->graph->get_statements(undef, $s, undef)->each(sub {
 633					$cl->store_triple($_[0]->subject, $o, $_[0]->object);
 634				});
 635			},
 636		'eq-rep-p'
 637		),
 638
 639	# eq-rep-o
 640	RDF::Closure::Rule::StatementMatcher->new(
 641		[undef, $OWL->sameAs, undef],
 642		sub {
 643				my ($cl, $st, $rule) = @_; my ($s, $p, $o) = $st->nodes;
 644				$cl->graph->get_statements(undef, undef, $s)->each(sub {
 645					$cl->store_triple($_[0]->subject, $_[0]->predicate, $o);
 646				});
 647			},
 648		'eq-rep-o'
 649		),
 650
 651	# eq-diff
 652	RDF::Closure::Rule::StatementMatcher->new(
 653		[undef, $OWL->sameAs, undef],
 654		sub {
 655				my ($cl, $st, $rule) = @_; my ($s, $p, $o) = $st->nodes;
 656				$cl->add_error("'sameAs' and 'differentFrom' cannot be used on the same subject-object pair: (%s, %s)", $s, $o)
 657					if $cl->graph->count_statements($s, $OWL->differentFrom, $o)
 658					|| $cl->graph->count_statements($o, $OWL->differentFrom, $s);
 659			},
 660		'eq-diff'
 661		),
 662
 663	# eq-diff2 and eq-diff3
 664	RDF::Closure::Rule::StatementMatcher->new(
 665		[undef, $RDF->type, $OWL->AllDifferent],
 666		sub {
 667				my ($cl, $st, $rule) = @_; my ($s, $p, $o) = $st->nodes;
 668				my $x = $s;
 669				my @m1 = $cl->graph->objects($x, $OWL->members);
 670				my @m2 = $cl->graph->objects($x, $OWL->distinctMembers);
 671				LOOPY: foreach my $y ((@m1, @m2))
 672				{
 673					my @zis = $cl->graph->get_list($y);
 674					LOOPI: foreach my $i (0 .. scalar(@zis)-1)
 675					{
 676						my $zi = $zis[$i];
 677						LOOPJ: foreach my $j ($i+1 .. scalar(@zis)-1)
 678						{
 679							my $zj = $zis[$j];
 680							next LOOPJ if $zi->equal($zj); # caught by another rule
 681							
 682							$cl->add_error("'sameAs' and 'AllDifferent' cannot be used on the same subject-object pair: (%s, %s)", $zi, $zj)
 683								if $cl->graph->count_statements($zi, $OWL->sameAs, $zj)
 684								|| $cl->graph->count_statements($zj, $OWL->sameAs, $zi);
 685						}
 686					}
 687				}
 688			},
 689		'eq-diff2, eq-diff3'
 690		),
 691
 692	# Ivan doesn't seem to have this rule, but it's required by test cases.
 693	# { ?x1 owl:differentFrom ?x2 . } => { ?x2 owl:differentFrom ?x1 . } .
 694	RDF::Closure::Rule::StatementMatcher->new(
 695		[undef, $OWL->differentFrom, undef],
 696		sub {
 697				my ($cl, $st, $rule) = @_;
 698				my ($x1, undef, $x2) = $st->nodes;
 699				$cl->store_triple($x2, $OWL->differentFrom, $x1);
 700			},
 701		'????'
 702		),
 703
 704	# cls-nothing2
 705	RDF::Closure::Rule::StatementMatcher->new(
 706		[undef, $RDF->type, $OWL->Nothing],
 707		sub {
 708				my ($cl, $st, $rule) = @_;
 709				$cl->add_error("%s is defined of type 'Nothing'", $st->subject);
 710			},
 711		'cls-nothing'
 712		),
 713
 714	# cls-int1, cls-int2
 715	RDF::Closure::Rule::StatementMatcher->new(
 716		[undef, $OWL->intersectionOf, undef],
 717		sub {
 718				my ($cl, $st, $rule) = @_;
 719				my ($c, undef, $x) = $st->nodes;
 720				my @classes = $cl->graph->get_list($x);
 721				return unless @classes;
 722				# cls-int1
 723				foreach my $y ($cl->graph->subjects($RDF->type, $classes[0]))
 724				{
 725					my $isInIntersection = TRUE;
 726					unless ($cl->graph->count_statements($y, $RDF->type, $c)) # Ivan doesn't do this check
 727					{
 728						CI: foreach my $ci (@classes[1 .. scalar(@classes)-1])
 729						{
 730							unless ($cl->graph->count_statements($y, $RDF->type, $ci))
 731							{
 732								$isInIntersection = FALSE;
 733								last CI;
 734							}
 735						}
 736						if ($isInIntersection)
 737						{
 738							$cl->store_triple($y, $RDF->type, $c);
 739						}
 740					}
 741				}
 742				# cls-int2
 743				foreach my $y ($cl->graph->subjects($RDF->type, $c))
 744				{
 745					$cl->store_triple($y, $RDF->type, $_) foreach @classes;
 746				}
 747			},
 748		'cls-int1, cls-int2'
 749		),
 750
 751	RDF::Closure::Rule::StatementMatcher->new(
 752		[undef, $OWL->unionOf, undef],
 753		sub {
 754				my ($cl, $st, $rule) = @_;
 755				my ($c, undef, $x) = $st->nodes;
 756				my @classes = $cl->graph->get_list($x);
 757				foreach my $cu (@classes)
 758				{
 759					$cl->graph->subjects($RDF->type, $cu)->each(sub {
 760						$cl->store_triple($_[0], $RDF->type, $c);
 761					});
 762				}
 763			},
 764		'cls-uni'
 765		),
 766
 767	RDF::Closure::Rule::StatementMatcher->new(
 768		[undef, $OWL->complementOf, undef],
 769		sub {
 770				my ($cl, $st, $rule) = @_;
 771				my ($c1, undef, $c2) = $st->nodes;
 772				$cl->graph->subjects($RDF->type, $c1)->each(sub{
 773					$cl->add_error("Violation of complementarity for classes %s and %s on element %s", $c1, $c2, $_[0])
 774						if $cl->graph->count_statements($_[0], $RDF->type, $c2);
 775				});
 776			},
 777		'cls-comm'
 778		),
 779
 780	RDF::Closure::Rule::StatementMatcher->new(
 781		[undef, $OWL->someValuesFrom, undef],
 782		sub {
 783				my ($cl, $st, $rule) = @_;
 784				my ($xx, undef, $y) = $st->nodes;
 785				$cl->graph->objects($xx, $OWL->onProperty)->each(sub{
 786					my $pp = shift;
 787					$cl->graph->get_statements(undef, $pp, undef)->each(sub{
 788						my ($u, undef, $v) = $_[0]->nodes;
 789						if ($y->equal($OWL->Thing) or $cl->graph->count_statements($u, $RDF->type, $y))
 790						{
 791							$cl->store_triple($u, $RDF->type, $xx);
 792						}
 793					});
 794				});				
 795			},
 796		'cls-svf1, cls-svf2'
 797		),
 798
 799	RDF::Closure::Rule::StatementMatcher->new(
 800		[undef, $OWL->allValuesFrom, undef],
 801		sub {
 802				my ($cl, $st, $rule) = @_;
 803				my ($xx, undef, $y) = $st->nodes;
 804				$cl->graph->objects($xx, $OWL->onProperty)->each(sub{
 805					my $pp = shift;
 806					$cl->graph->subjects($RDF->type, $xx)->each(sub{
 807						my $u = shift;
 808						$cl->graph->objects($u, $pp)->each(sub{
 809							my $v = shift;
 810							$cl->store_triple($v, $RDF->type, $y);
 811						});
 812					});
 813				});				
 814			},
 815		'cls-avf'
 816		),
 817
 818	RDF::Closure::Rule::StatementMatcher->new(
 819		[undef, $OWL->hasValue, undef],
 820		sub {
 821				my ($cl, $st, $rule) = @_;
 822				my ($xx, undef, $y) = $st->nodes;
 823				$cl->graph->objects($xx, $OWL->onProperty)->each(sub{
 824					my $pp = shift;
 825					$cl->graph->subjects($RDF->type, $xx)->each(sub{
 826						my $u = shift;
 827						$cl->store_triple($u, $pp, $y);
 828					});
 829					$cl->graph->subjects($pp, $y)->each(sub{
 830						my $u = shift;
 831						$cl->store_triple($u, $RDF->type, $xx);
 832					});
 833				});				
 834			},
 835		'cls-hv1, cls-hv2'
 836		),
 837
 838	RDF::Closure::Rule::StatementMatcher->new(
 839		[undef, $OWL->maxCardinality, undef],
 840		sub {
 841				my ($cl, $st, $rule) = @_;
 842				my ($xx, undef, $x) = $st->nodes;
 843				my $val = int( $x->is_literal ? $x->literal_value : -1 );
 844				# maxc1
 845				if ($val == 0)
 846				{
 847					$cl->graph->objects($xx, $OWL->onProperty)->each(sub{
 848						my $pp = shift;
 849						$cl->graph->get_statements(undef, $pp, undef)->each(sub{
 850							my ($u, undef, $y) = $_[0]->nodes;
 851							$cl->add_error("Erronous usage of maximum cardinality with %s, %s", $xx, $y)
 852								if $cl->graph->count_statements($u, $RDF->type, $xx);
 853						});
 854					});				
 855				}
 856				# maxc2
 857				elsif ($val == 1)
 858				{
 859					$cl->graph->objects($xx, $OWL->onProperty)->each(sub{
 860						my $pp = shift;
 861						$cl->graph->get_statements(undef, $pp, undef)->each(sub{
 862							my ($u, undef, $y1) = $_[0]->nodes;
 863							if ($cl->graph->count_statements($u, $RDF->type, $xx))
 864							{
 865								$cl->graph->objects($u, $pp)->each(sub{
 866									my $y2 = shift;
 867									unless ($y1->equal($y2))
 868									{
 869										$cl->store_triple($y1, $OWL->sameAs, $y2);
 870										$cl->store_triple($y2, $OWL->sameAs, $y1);
 871									}
 872								});
 873							}
 874						});
 875					});				
 876				}
 877				else
 878				{
 879					# awesome, we can't do anything!
 880				}
 881			},
 882		'cls-maxc1, cls-maxc2'
 883		),
 884
 885	RDF::Closure::Rule::StatementMatcher->new(
 886		[undef, $OWL->maxCardinality, undef],
 887		sub {
 888				my ($cl, $st, $rule) = @_;
 889				my ($xx, undef, $x) = $st->nodes;
 890				my $val = int( $x->is_literal ? $x->literal_value : -1 );
 891				# cls-maxqc1 and cls-maxqc2
 892				if ($val == 0)
 893				{
 894					my @cc = $cl->graph->objects($xx, $OWL->onClass);
 895					$cl->graph->objects($xx, $OWL->onProperty)->each(sub{
 896						my $pp = shift;
 897						foreach my $cc (@cc)
 898						{
 899							$cl->graph->get_statements(undef, $pp, undef)->each(sub{
 900								my ($u, undef, $y) = $_[0]->nodes;
 901								$cl->add_error("Erronous usage of maximum qualified cardinality with %s, %s, and %s", $xx, $cc, $y)
 902									if $cl->graph->count_statements($u, $RDF->type, $xx)
 903									&& ($cc->equal($OWL->Thing) or $cl->graph->count_statements($y, $RDF->type, $cc));
 904							});
 905						}
 906					});				
 907				}
 908				# cls-maxqc3 and cls-maxqc4
 909				elsif ($val == 1)
 910				{
 911					my @cc = $cl->graph->objects($xx, $OWL->onClass);
 912					$cl->graph->objects($xx, $OWL->onProperty)->each(sub{
 913						my $pp = shift;
 914						foreach my $cc (@cc)
 915						{
 916							$cl->graph->get_statements(undef, $pp, undef)->each(sub{
 917								my ($u, undef, $y1) = $_[0]->nodes;
 918								if ($cl->graph->count_statements($u, $RDF->type, $xx))
 919								{
 920									if ($cc->equal($OWL->Thing))
 921									{
 922										$cl->graph->objects($u, $pp)->each(sub{
 923											my $y2 = shift;
 924											unless ($y1->equal($y2))
 925											{
 926												$cl->store_triple($y1, $OWL->sameAs, $y2);
 927												$cl->store_triple($y2, $OWL->sameAs, $y1);
 928											}
 929										});
 930									}
 931									elsif ($cl->graph->count_statements($y1, $RDF->type, $cc))
 932									{
 933										$cl->graph->objects($u, $pp)->each(sub{
 934											my $y2 = shift;
 935											if (!$y1->equal($y2)
 936											and $cl->graph->count_statements($y2, $RDF->type, $cc))
 937											{
 938												$cl->store_triple($y1, $OWL->sameAs, $y2);
 939												$cl->store_triple($y2, $OWL->sameAs, $y1);
 940											}
 941										});
 942									}
 943								}
 944							});
 945						}
 946					});				
 947				}
 948				else
 949				{
 950					# awesome, we can't do anything!
 951				}
 952			},
 953		'cls-maxqc1, cls-maxqc2, cls-maxqc3, cls-maxqc4'
 954		),
 955
 956	RDF::Closure::Rule::StatementMatcher->new(
 957		[undef, $OWL->oneOf, undef],
 958		sub {
 959				my ($cl, $st, $rule) = @_;
 960				my ($c, undef, $x) = $st->nodes;
 961				my @indivs = $cl->graph->get_list($x);
 962				foreach my $i (@indivs)
 963				{
 964					$cl->store_triple($i, $RDF->type, $c);
 965				}
 966			},
 967		'cls-oo'
 968		),
 969
 970	RDF::Closure::Rule::StatementMatcher->new(
 971		[undef, $RDFS->subClassOf, undef],
 972		sub {
 973				my ($cl, $st, $rule) = @_;
 974				my ($c1, undef, $c2) = $st->nodes;
 975				unless ($c1->equal($c2))
 976				{
 977					$cl->graph->subjects($RDF->type, $c1)->each(sub {
 978						$cl->store_triple($_[0], $RDF->type, $c2);
 979					});
 980				}
 981			},
 982		'cax-sco'
 983		),
 984
 985	RDF::Closure::Rule::StatementMatcher->new(
 986		[undef, $OWL->equivalentClass, undef],
 987		sub {
 988				my ($cl, $st, $rule) = @_;
 989				my ($c1, undef, $c2) = $st->nodes;
 990				$cl->store_triple($c2, $OWL->equivalentClass, $c1); # Toby added
 991				$cl->store_triple($c1, $RDFS->subClassOf, $c2);
 992				$cl->store_triple($c2, $RDFS->subClassOf, $c1);
 993				unless ($c1->equal($c2))
 994				{
 995					$cl->graph->subjects($RDF->type, $c1)->each(sub {
 996						$cl->store_triple($_[0], $RDF->type, $c2);
 997					});
 998					$cl->graph->subjects($RDF->type, $c2)->each(sub {
 999						$cl->store_triple($_[0], $RDF->type, $c1);
1000					});
1001				}
1002			},
1003		'cax-eqc, cax-eqc1'
1004		),
1005
1006	RDF::Closure::Rule::StatementMatcher->new(
1007		[undef, $OWL->disjointWith, undef],
1008		sub {
1009				my ($cl, $st, $rule) = @_;
1010				my ($c1, undef, $c2) = $st->nodes;
1011				$cl->graph->subjects($RDF->type, $c1)->each(sub {
1012					$cl->add_error('Disjoint classes %s and %s have a common individual %s', $c1, $c2, $_[0])
1013						if $cl->graph->count_statements($_[0], $RDF->type, $c2);
1014				});
1015			},
1016		'cax-dw'
1017		),
1018
1019	RDF::Closure::Rule::StatementMatcher->new(
1020		[undef, $RDF->type, $OWL->AllDisjointClasses],
1021		sub {
1022				my ($cl, $st, $rule) = @_;
1023				my $x = $st->subject;
1024				$cl->graph->objects($x, $OWL->members)->each(sub{
1025					my @classes = $cl->graph->get_list($_[0]);
1026					if (@classes)
1027					{
1028						for my $i (0 .. scalar(@classes)-1)
1029						{
1030							my $cl1 = $classes[$i];
1031							$cl->graph->subjects($RDF->type, $cl1)->each(sub{
1032								my $z = shift;
1033								for my $j ($i+1 .. scalar(@classes)-1)
1034								{
1035									my $cl2 = $classes[$j];
1036									$cl->add_error("Disjoint classes %s and %s have a common individual %s", $cl1, $cl2, $z)
1037										if $cl->graph->count_statements($z, $RDF->type, $cl2);
1038								}
1039							});
1040						}
1041					}
1042				});
1043			},
1044		'cax-adc'
1045		),
1046	
1047	RDF::Closure::Rule::StatementMatcher->new(
1048		[undef, $RDF->type, $OWL->Class],
1049		sub {
1050				my ($cl, $st, $rule) = @_;
1051				my ($c) = $st->nodes;
1052				$cl->store_triple($c, $RDFS->subClassOf, $c);
1053				$cl->store_triple($c, $OWL->equivalentClass, $c);
1054				$cl->store_triple($c, $RDFS->subClassOf, $OWL->Thing);
1055				$cl->store_triple($OWL->Nothing, $RDFS->subClassOf, $c);
1056			},
1057		'scm-cls'
1058		),
1059
1060	RDF::Closure::Rule::StatementMatcher->new(
1061		[undef, $RDFS->subClassOf, undef],
1062		sub {
1063				my ($cl, $st, $rule) = @_;
1064				my ($c1, undef, $c2) = $st->nodes;
1065				$cl->graph->objects($c2, $RDFS->subClassOf)->each(sub {
1066					my $c3 = $_[0];
1067					if ($c1->equal($c3))
1068					{
1069						# scm-eqc2
1070						$cl->store_triple($c1, $OWL->equivalentClass, $c3);
1071					}
1072					else
1073					{
1074						# scm-sco
1075						$cl->store_triple($c1, $RDFS->subClassOf, $c3);
1076					}
1077					# Ivan could optimise his version better.
1078				});
1079			},
1080		'scm-sco, scm-eqc2'
1081		),
1082	
1083	RDF::Closure::Rule::StatementMatcher->new(
1084		[undef, $RDF->type, $OWL->ObjectProperty],
1085		sub {
1086				my ($cl, $st, $rule) = @_;
1087				my ($pp) = $st->nodes;
1088				$cl->store_triple($pp, $RDFS->subPropertyOf, $pp);
1089				$cl->store_triple($pp, $OWL->equivalentProperty, $pp);
1090			},
1091		'scm-op'
1092		),
1093		
1094	RDF::Closure::Rule::StatementMatcher->new(
1095		[undef, $RDF->type, $OWL->DatatypeProperty],
1096		sub {
1097				my ($cl, $st, $rule) = @_;
1098				my ($pp) = $st->nodes;
1099				$cl->store_triple($pp, $RDFS->subPropertyOf, $pp);
1100				$cl->store_triple($pp, $OWL->equivalentProperty, $pp);
1101			},
1102		'scm-dp'
1103		),
1104		
1105	RDF::Closure::Rule::StatementMatcher->new(
1106		[undef, $RDF->type, $RDF->Property],
1107		sub {
1108				my ($cl, $st, $rule) = @_;
1109				my ($pp) = $st->nodes;
1110				$cl->store_triple($pp, $RDFS->subPropertyOf, $pp);
1111				$cl->store_triple($pp, $OWL->equivalentProperty, $pp);
1112			},
1113		'????' # Ivan made this up
1114		),
1115		
1116	RDF::Closure::Rule::StatementMatcher->new(
1117		[undef, $OWL->equivalentProperty, undef],
1118		sub {
1119				my ($cl, $st, $rule) = @_;
1120				my ($p1, undef, $p2) = $st->nodes;
1121				$cl->store_triple($p2, $OWL->equivalentProperty, $p1); # Toby added
1122				$cl->store_triple($p1, $RDFS->subPropertyOf, $p2); 
1123				$cl->store_triple($p2, $RDFS->subPropertyOf, $p1); 
1124				unless ($p1->equal($p2))
1125				{
1126					$cl->graph->subjects($RDF->type, $p1)->each(sub {
1127						$cl->store_triple($_[0], $RDF->type, $p2);
1128					});
1129					$cl->graph->subjects($RDF->type, $p2)->each(sub {
1130						$cl->store_triple($_[0], $RDF->type, $p1);
1131					});
1132				}
1133			},
1134		'cax-eqp, cax-eqp1'
1135		),
1136
1137	RDF::Closure::Rule::StatementMatcher->new(
1138		[undef, $RDFS->subPropertyOf, undef],
1139		sub {
1140				my ($cl, $st, $rule) = @_;
1141				my ($p1, undef, $p2) = $st->nodes;
1142				$cl->graph->objects($p2, $RDFS->subPropertyOf)->each(sub {
1143					my $p3 = $_[0];
1144					if ($p1->equal($p3))
1145					{
1146						# scm-eqp2
1147						$cl->store_triple($p1, $OWL->equivalentProperty, $p3);
1148					}
1149					else
1150					{
1151						# scm-spo
1152						$cl->store_triple($p1, $RDFS->subPropertyOf, $p3);
1153					}
1154					# Ivan could optimise his version better.
1155				});
1156			},
1157		'scm-spo, scm-eqp2'
1158		),
1159	
1160	RDF::Closure::Rule::StatementMatcher->new(
1161		[undef, $RDFS->domain, undef],
1162		sub {
1163				my ($cl, $st, $rule) = @_;
1164				my ($pp, undef, $c1) = $st->nodes;
1165				$cl->graph->objects($c1, $RDFS->subClassOf)->each(sub {
1166					my $c2 = $_[0];
1167					$cl->store_triple($pp, $RDFS->domain, $c2) unless $c1->equal($c2);
1168				});
1169				my ($p2, undef, $c) = $st->nodes;
1170				$cl->graph->subjects($RDFS->subPropertyOf, $p2)->each(sub {
1171					my $p1 = $_[0];
1172					$cl->store_triple($p1, $RDFS->domain, $c) unless $p1->equal($p2);
1173				});
1174			},
1175		'scm-dom1, scm-dom2'
1176		),
1177
1178	RDF::Closure::Rule::StatementMatcher->new(
1179		[undef, $RDFS->range, undef],
1180		sub {
1181				my ($cl, $st, $rule) = @_;
1182				my ($pp, undef, $c1) = $st->nodes;
1183				$cl->graph->objects($c1, $RDFS->subClassOf)->each(sub {
1184					my $c2 = $_[0];
1185					$cl->store_triple($pp, $RDFS->range, $c2) unless $c1->equal($c2);
1186				});
1187				my ($p2, undef, $c) = $st->nodes;
1188				$cl->graph->subjects($RDFS->subPropertyOf, $p2)->each(sub {
1189					my $p1 = $_[0];
1190					$cl->store_triple($p1, $RDFS->range, $c) unless $p1->equal($p2);
1191				});
1192			},
1193		'scm-rng1, scm-rng2'
1194		),
1195
1196	RDF::Closure::Rule::StatementMatcher->new(
1197		[undef, $OWL->hasValue, undef],
1198		sub {
1199				my ($cl, $st, $rule) = @_;
1200				my ($c1, undef, $i)  = $st->nodes;
1201				my @p1 = $cl->graph->objects($c1, $OWL->onProperty);
1202				my @c2 = $cl->graph->subjects($OWL->hasValue, $i);
1203				foreach my $p1 (@p1)
1204				{
1205					foreach my $c2 (@c2)
1206					{
1207						foreach my $p2 ($cl->graph->objects($c2, $OWL->onProperty))
1208						{
1209							$cl->store_triple($c1, $RDFS->subClassOf, $c2)
1210								if $cl->graph->count_statements($p1, $RDFS->subPropertyOf, $p2);
1211						}
1212					}
1213				}
1214			},
1215		'scm-hv'
1216		),
1217		
1218	RDF::Closure::Rule::StatementMatcher->new(
1219		[undef, $OWL->someValuesFrom, undef],
1220		sub {
1221				my ($cl, $st, $rule) = @_;
1222				my ($xx, undef, $y) = $st->nodes;
1223				$cl->graph->objects($xx, $OWL->onProperty)->each(sub{
1224					my $pp = shift;
1225					$cl->graph->get_statements(undef, $pp, undef)->each(sub{
1226						my ($u, undef, $v) = (shift)->nodes;
1227						if ($y->equal($OWL->Thing) or $cl->graph->count_statements($v, $RDF->type, $y))
1228						{
1229							$cl->store_triple($u, $RDF->type, $xx);
1230						}
1231					});
1232				});
1233			},
1234		'scm-svf1, scm-svf2'
1235		),
1236		
1237	RDF::Closure::Rule::StatementMatcher->new(
1238		[undef, $OWL->allValuesFrom, undef],
1239		sub {
1240				my ($cl, $st, $rule) = @_;
1241				my ($xx, undef, $y) = $st->nodes;
1242				$cl->graph->objects($xx, $OWL->onProperty)->each(sub{
1243					my $pp = shift;
1244					$cl->graph->subjects($RDF->type, $xx)->each(sub {
1245						my $u = shift;
1246						$cl->graph->objects($u, $pp)->each(sub {
1247							my $v = shift;
1248							$cl->store_triple($v, $RDF->type, $y);
1249						});
1250					});
1251				});
1252			},
1253		'scm-avf'
1254		),
1255			
1256	# scm-int
1257	RDF::Closure::Rule::StatementMatcher->new(
1258		[undef, $OWL->intersectionOf, undef],
1259		sub {
1260				my ($cl, $st, $rule) = @_;
1261				my ($c, undef, $x) = $st->nodes;
1262				$cl->store_triple($c, $RDFS->subClassOf, $_) foreach $cl->graph->get_list($x);
1263			},
1264		'scm-int'
1265		),
1266	
1267	# scm-uni
1268	RDF::Closure::Rule::StatementMatcher->new(
1269		[undef, $OWL->unionOf, undef],
1270		sub {
1271				my ($cl, $st, $rule) = @_;
1272				my ($c, undef, $x) = $st->nodes;
1273				$cl->store_triple($_, $RDFS->subClassOf, $c) foreach $cl->graph->get_list($x);
1274			},
1275		'scm-uni'
1276		),
1277		
1278	);
1279
1280sub _property_chain
1281{
1282	my ($self, $p, $x) = @_;
1283	
1284	my @chain = $self->graph->get_list($x);
1285	return unless @chain;
1286	
1287	$self->graph->get_statements(undef, $chain[0], undef)->each(sub {
1288		my ($u1, $_y, $_z) = $_[0]->nodes;
1289		
1290		my $finalList   = [[$u1,$_z]];
1291		my $chainExists = TRUE;
1292		
1293		PI: foreach my $pi (@chain[1 .. scalar(@chain)-1])
1294		{
1295			my $newList = [];
1296			foreach my $q (@$finalList)
1297			{
1298				my ($_u, $ui) = @$q;
1299				foreach my $u ($self->graph->objects($ui, $pi))
1300				{
1301					push @$newList, [$u1, $u];
1302				}
1303			}
1304			if (@$newList)
1305			{
1306				$finalList = $newList;
1307			}
1308			else
1309			{
1310				$chainExists = FALSE;
1311				last PI;
1312			}
1313		}
1314		if ($chainExists)
1315		{
1316			foreach my $q (@$finalList)
1317			{
1318				my ($_u, $un) = @$q;
1319				$self->store_triple(($u1, $p, $un));
1320			}
1321		}
1322	});
1323}
1324
1325sub __init__
1326{
1327	my ($self, @args) = @_;
1328	$self->SUPER::__init__(@args);
1329	$self->{bnodes} = [];
1330	$self->{options}{technique} = 'RULE';
1331	return $self;
1332}
1333
1334sub _get_resource_or_literal
1335{
1336	my ($self, $node) = @_;
1337	$node; # ????
1338}
1339
1340sub post_process
1341{
1342	# Python version removes bnode predicate triples, but I'm going to keep them.
1343}
1344
1345sub add_axioms
1346{
1347	my ($self) = @_;
1348	$self->store_triple(statement($_->nodes, $self->{axiom_context}))
1349		foreach @$OWLRL_Axiomatic_Triples;
1350}
1351
1352sub add_daxioms
1353{
1354	my ($self) = @_;
1355	$self->store_triple(statement($_->nodes, $self->{daxiom_context}))
1356		foreach @$OWLRL_D_Axiomatic_Triples;
1357}
1358
1359sub entailment_regime
1360{
1361	return 'http://www.w3.org/ns/owl-profile/RL';
1362}
1363
13641;
1365
1366=head1 NAME
1367
1368RDF::Closure::Engine::OWL2RL - OWL 2 RL inference
1369
1370=head1 ANALOGOUS PYTHON
1371
1372RDFClosure/OWLRL.py
1373
1374=head1 DESCRIPTION
1375
1376Performs OWL 2 inference, using the RL profile of OWL.
1377
1378=head1 SEE ALSO
1379
1380L<RDF::Closure::Engine>.
1381
1382L<http://www.perlrdf.org/>.
1383
1384L<http://www.w3.org/TR/2009/REC-owl2-profiles-20091027/#OWL_2_RL>.
1385
1386=head1 AUTHOR
1387
1388Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
1389
1390=head1 COPYRIGHT
1391
1392Copyright 2008-2011 Ivan Herman
1393
1394Copyright 2011-2012 Toby Inkster
1395
1396This library is free software; you can redistribute it and/or modify it
1397under any of the following licences:
1398
1399=over
1400
1401=item * The Artistic License 1.0 L<http://www.perlfoundation.org/artistic_license_1_0>.
1402
1403=item * The GNU General Public License Version 1 L<http://www.gnu.org/licenses/old-licenses/gpl-1.0.txt>,
1404or (at your option) any later version.
1405
1406=item * The W3C Software Notice and License L<http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231>.
1407
1408=item * The Clarified Artistic License L<http://www.ncftp.com/ncftp/doc/LICENSE.txt>.
1409
1410=back
1411
1412
1413=head1 DISCLAIMER OF WARRANTIES
1414
1415THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
1416WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
1417MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1418
1419
1420=cut
1421