Commits

Toby Inkster committed 36889db

Release utility functions 0.001.

  • Participants

Comments (0)

Files changed (13)

+############################################################################
+## Changes #################################################################
+############################################################################
+
+RDF-Query-Functions-Buzzword-Util
+=================================
+
+Created:      2011-01-04
+Home page:    <http://search.cpan.org/dist/RDF-Query-Functions-Buzzword-Util/>
+Bug tracker:  <http://rt.cpan.org/Dist/Display.html?Queue=RDF-Query-Functions-Buzzword-Util>
+Maintainer:   Toby Inkster <mailto:tobyink@cpan.org>
+
+0.001 [2011-01-05]
+
+#############################################################
+
+@prefix :        <http://usefulinc.com/ns/doap#> .
+@prefix dcs:     <http://ontologi.es/doap-changeset#> .
+@prefix dc:      <http://purl.org/dc/terms/> .
+@prefix foaf:    <http://xmlns.com/foaf/0.1/> .
+@prefix my:      <http://purl.org/NET/cpan-uri/dist/RDF-Query-Functions-Buzzword-Util/> .
+@prefix rdfs:    <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix toby:    <http://tobyinkster.co.uk/#> .
+@prefix xsd:     <http://www.w3.org/2001/XMLSchema#> .
+
+#############################################################
+
+<>
+
+	dc:title         "Changes" ;
+	dc:description   "Revision history for Perl extension RDF::Query::Functions::Buzzword::Util."@en ;
+	dc:subject       my:project ;
+	dc:creator       toby:i .
+
+#############################################################
+
+my:v_0-001
+
+	a               :Version ;
+	dc:issued       "2011-01-05"^^xsd:date ;
+	:revision       "0.001"^^xsd:string ;
+	:file-release   <http://backpan.cpan.org/authors/id/T/TO/TOBYINK/RDF-Query-Functions-Buzzword-Util-0.001.tar.gz> ;
+	rdfs:comment    "Original version"@en .
+
+#############################################################
+
+my:project
+
+	a               :Project ;
+	:name           "RDF-Query-Functions-Buzzword-Util" ;
+	:shortdesc      "plugin for buzzword.org.uk utility functions"@en ;
+	:programming-language  "Perl" ;
+	:homepage       <http://search.cpan.org/dist/RDF-Query-Functions-Buzzword-Util/> ;
+	:download-page  <http://search.cpan.org/dist/RDF-Query-Functions-Buzzword-Util/> ;
+	:bug-database   <http://rt.cpan.org/Dist/Display.html?Queue=RDF-Query-Functions-Buzzword-Util> ;
+	:repository     [ a :SVNRepository ; :browse <http://goddamn.co.uk/viewvc/perlmods/RDF-Query-Functions-Buzzword-Util/> ] ;
+	:maintainer     toby:i ;
+	:developer      toby:i ;
+	:documenter     toby:i ;
+	:tester         toby:i ;
+	:created        "2011-01-04"^^xsd:date ;
+	:license        <http://dev.perl.org/licenses/> ;
+	:release        my:v_0-001 ;
+	rdfs:seeAlso    <http://buzzword.org.uk/2011/functions/util> .
+
+#############################################################
+
+toby:i
+
+	a               foaf:Person ;
+	foaf:name       "Toby Inkster" ;
+	foaf:homepage   <http://tobyinkster.co.uk/> ;
+	foaf:page       <http://search.cpan.org/~tobyink/> ;
+	foaf:mbox       <mailto:tobyink@cpan.org> ;
+	<http://www.w3.org/2002/07/owl#sameAs> <http://purl.org/NET/cpan-uri/person/tobyink> .
+
+#############################################################
+<?xml version="1.0" encoding="utf-8"?>
+<rdf:RDF xmlns:dbug="http://ontologi.es/doap-bugs#" xmlns:dc="http://purl.org/dc/terms/" xmlns:dcs="http://ontologi.es/doap-changeset#" xmlns:doap="http://usefulinc.com/ns/doap#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
+<rdf:Description rdf:nodeID="rE7ACE3C818A111E084309E47CFDE21D6r0">
+	<doap:browse rdf:resource="http://goddamn.co.uk/viewvc/perlmods/RDF-Query-Functions-Buzzword-Util/"/>
+	<rdf:type rdf:resource="http://usefulinc.com/ns/doap#SVNRepository"/>
+</rdf:Description>
+<rdf:Description rdf:about="file:///home/tai/src/perlmods/RDF-Query-Functions-Buzzword-Util/Changes.ttl">
+	<dc:creator rdf:resource="http://tobyinkster.co.uk/#i"/>
+	<dc:description xml:lang="en">Revision history for Perl extension RDF::Query::Functions::Buzzword::Util.</dc:description>
+	<dc:subject rdf:resource="http://purl.org/NET/cpan-uri/dist/RDF-Query-Functions-Buzzword-Util/project"/>
+	<dc:title>Changes</dc:title>
+</rdf:Description>
+<rdf:Description rdf:about="http://purl.org/NET/cpan-uri/dist/RDF-Query-Functions-Buzzword-Util/project">
+	<doap:bug-database rdf:resource="http://rt.cpan.org/Dist/Display.html?Queue=RDF-Query-Functions-Buzzword-Util"/>
+	<doap:created rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2011-01-04</doap:created>
+	<doap:developer rdf:resource="http://tobyinkster.co.uk/#i"/>
+	<doap:documenter rdf:resource="http://tobyinkster.co.uk/#i"/>
+	<doap:download-page rdf:resource="http://search.cpan.org/dist/RDF-Query-Functions-Buzzword-Util/"/>
+	<doap:homepage rdf:resource="http://search.cpan.org/dist/RDF-Query-Functions-Buzzword-Util/"/>
+	<doap:license rdf:resource="http://dev.perl.org/licenses/"/>
+	<doap:maintainer rdf:resource="http://tobyinkster.co.uk/#i"/>
+	<doap:name>RDF-Query-Functions-Buzzword-Util</doap:name>
+	<doap:programming-language>Perl</doap:programming-language>
+	<doap:release rdf:resource="http://purl.org/NET/cpan-uri/dist/RDF-Query-Functions-Buzzword-Util/v_0-001"/>
+	<doap:repository rdf:nodeID="rE7ACE3C818A111E084309E47CFDE21D6r0"/>
+	<doap:shortdesc xml:lang="en">plugin for buzzword.org.uk utility functions</doap:shortdesc>
+	<doap:tester rdf:resource="http://tobyinkster.co.uk/#i"/>
+	<rdf:type rdf:resource="http://usefulinc.com/ns/doap#Project"/>
+	<rdfs:seeAlso rdf:resource="http://buzzword.org.uk/2011/functions/util"/>
+</rdf:Description>
+<rdf:Description rdf:about="http://purl.org/NET/cpan-uri/dist/RDF-Query-Functions-Buzzword-Util/v_0-001">
+	<dc:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2011-01-05</dc:issued>
+	<doap:file-release rdf:resource="http://backpan.cpan.org/authors/id/T/TO/TOBYINK/RDF-Query-Functions-Buzzword-Util-0.001.tar.gz"/>
+	<doap:revision rdf:datatype="http://www.w3.org/2001/XMLSchema#string">0.001</doap:revision>
+	<rdf:type rdf:resource="http://usefulinc.com/ns/doap#Version"/>
+	<rdfs:comment xml:lang="en">Original version</rdfs:comment>
+</rdf:Description>
+<rdf:Description xmlns:ns1="http://www.w3.org/2002/07/owl#" rdf:about="http://tobyinkster.co.uk/#i">
+	<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
+	<ns1:sameAs rdf:resource="http://purl.org/NET/cpan-uri/person/tobyink"/>
+	<foaf:homepage rdf:resource="http://tobyinkster.co.uk/"/>
+	<foaf:mbox rdf:resource="mailto:tobyink@cpan.org"/>
+	<foaf:name>Toby Inkster</foaf:name>
+	<foaf:page rdf:resource="http://search.cpan.org/~tobyink/"/>
+</rdf:Description>
+</rdf:RDF>
+Changes
+Changes.ttl
+Changes.xml
+Makefile.PL
+MANIFEST
+MANIFEST.SKIP
+README
+META.yml
+SIGNATURE
+
+t/00sig.t
+t/01basic.t
+
+inc/Module/AutoInstall.pm
+inc/Module/Install/AutoInstall.pm
+inc/Module/Install/Base.pm
+inc/Module/Install/Can.pm
+inc/Module/Install/DOAPChangeSets.pm
+inc/Module/Install/Fetch.pm
+inc/Module/Install/Include.pm
+inc/Module/Install/Makefile.pm
+inc/Module/Install/Metadata.pm
+inc/Module/Install.pm
+inc/Module/Install/ReadmeFromPod.pm
+inc/Module/Install/Win32.pm
+inc/Module/Install/WriteAll.pm
+inc/Test/Signature.pm
+
+lib/RDF/Query/Functions/Buzzword/Util.pm
+^Makefile$
+^blib/
+^pm_to_blib
+^blibdirs
+\.svn
+^example.*\.pl$
+^[^/]+\.(tar\.gz|tar\.bz2|tgz|tbz2|tbz|zip|tar)$
+^MYMETA.yml
+---
+abstract: 'plugin for buzzword.org.uk utility functions'
+author:
+  - 'Toby Inkster <tobyink@cpan.org>'
+build_requires:
+  ExtUtils::MakeMaker: 6.42
+  Module::Signature: 0.66
+  Test::More: 0.61
+configure_requires:
+  ExtUtils::MakeMaker: 6.42
+distribution_type: module
+generated_by: 'Module::Install version 1.00'
+keywords:
+  - SPARQL
+  - query
+  - extension
+  - functions
+  - utilities
+  - utility
+  - plugin
+  - RDF
+  - Semantic
+  - Web
+  - SemWeb
+license: perl
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.4.html
+  version: 1.4
+name: RDF-Query-Functions-Buzzword-Util
+no_index:
+  directory:
+    - inc
+    - t
+requires:
+  Data::UUID: 0
+  RDF::Query: 2.903
+  Scalar::Util: 0
+  XML::LibXML: 1.60
+  common::sense: 0
+resources:
+  bugtracker: http://rt.cpan.org/Dist/Display.html?Queue=RDF-Query-Functions-Buzzword-Util
+  homepage: http://search.cpan.org/dist/RDF-Query-Functions-Buzzword-Util/
+  license: http://dev.perl.org/licenses/
+  repository: http://goddamn.co.uk/viewvc/perlmods/RDF-Query-Functions-Buzzword-Util/
+version: 0.001
+use strict;
+use warnings;
+
+use inc::Module::Install;
+
+my $dist = 'RDF-Query-Functions-Buzzword-Util';
+my $fn   = "lib/$dist.pm"; $fn =~ s#-#/#g;
+
+name                $dist;
+perl_version_from   $fn;
+version_from        $fn;
+abstract_from       $fn;
+readme_from         $fn;
+author              'Toby Inkster <tobyink@cpan.org>';
+license             'perl';
+
+requires            'common::sense'      => 0;
+requires            'Data::UUID'         => 0;
+test_requires       'Module::Signature'  => '0.66';
+requires            'RDF::Query'         => '2.903';
+requires            'Scalar::Util'       => 0;
+test_requires       'Test::More'         => '0.61';
+requires            'XML::LibXML'        => '1.60';
+
+resources(
+	'homepage'   => "http://search.cpan.org/dist/$dist/",
+	'repository' => "http://goddamn.co.uk/viewvc/perlmods/$dist/",
+	'bugtracker' => "http://rt.cpan.org/Dist/Display.html?Queue=$dist",
+	);
+
+keywords(qw[SPARQL query extension functions utilities utility plugin RDF Semantic Web SemWeb]);
+
+write_doap_changes;
+write_doap_changes_xml;
+
+include 'Test::Signature';
+auto_install;
+WriteAll(
+	'meta' => 1,
+	'sign' => 1,
+	);

RDF-Query-Functions-Buzzword-Util-0.001.tar.gz

Binary file added.
+NAME
+    RDF::Query::Functions::Buzzword::Util - plugin for buzzword.org.uk
+    utility functions
+
+SYNOPSIS
+      use RDF::TrineShortcuts qw[:all];
+      use Data::Dumper;
+  
+      my $data = rdf_parse(<<'TURTLE', type=>'turtle');
+      @prefix foaf: <http://xmlns.com/foaf/0.1/> .
+      @prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+  
+      <http://tobyinkster.co.uk/#i>
+        foaf:name "Toby Inkster" ;
+        foaf:page [ foaf:name "Toby Inkster" ] ;
+        foaf:junk "Foo <ex xmlns=\"urn:junk\">Bar</ex>"^^rdf:XMLLiteral ;
+        foaf:mbox <mailto:tobyink@cpan.org> .
+      TURTLE
+  
+      $r = rdf_query(<<'SPARQL', $data);
+      PREFIX foaf: <http://xmlns.com/foaf/0.1/>
+      PREFIX util: <http://buzzword.org.uk/2011/functions/util#>
+      PREFIX junk: <urn:junk> 
+      SELECT
+        ?name
+        (util:uc(?name) AS ?ucname)
+        (util:trim(util:sprintf(" I am %s "@en, ?name)) AS ?intro)
+        (util:skolem(?page, "oid") AS ?skolempage)
+        (util:preg_replace("t", "x", ?name, "ig") AS ?mangled)
+        (util:find_xpath("//junk:ex", ?junk, 0) AS ?found)
+      WHERE
+      {
+        ?person foaf:name ?name ; foaf:page ?page ; foaf:junk ?junk.
+      }
+      SPARQL
+  
+      print Dumper(flatten_iterator($r, literal_as=>'ntriples'));
+
+DESCRIPTION
+    This is a plugin for RDF::Query providing a number of extension
+    functions.
+
+    *   http://buzzword.org.uk/2011/functions/util#defragment
+
+    *   http://buzzword.org.uk/2011/functions/util#find_xpath
+
+    *   http://buzzword.org.uk/2011/functions/util#lc
+
+    *   http://buzzword.org.uk/2011/functions/util#ltrim
+
+    *   http://buzzword.org.uk/2011/functions/util#oid
+
+    *   http://buzzword.org.uk/2011/functions/util#oid_uri
+
+    *   http://buzzword.org.uk/2011/functions/util#preg_replace
+
+    *   http://buzzword.org.uk/2011/functions/util#rand
+
+    *   http://buzzword.org.uk/2011/functions/util#rtrim
+
+    *   http://buzzword.org.uk/2011/functions/util#skolem
+
+    *   http://buzzword.org.uk/2011/functions/util#sprintf
+
+    *   http://buzzword.org.uk/2011/functions/util#str_replace
+
+    *   http://buzzword.org.uk/2011/functions/util#trim
+
+    *   http://buzzword.org.uk/2011/functions/util#uc
+
+    *   http://buzzword.org.uk/2011/functions/util#uuid
+
+    *   http://buzzword.org.uk/2011/functions/util#uuid_uri
+
+SEE ALSO
+    RDF::Query.
+
+    <http://perlrdf.org/>.
+
+AUTHOR
+    Toby Inkster <tobyink@cpan.org>.
+
+COPYRIGHT
+    Copyright 2010-2011 Toby Inkster
+
+    This library is free software; you can redistribute it and/or modify it
+    under the same terms as Perl itself.
+
+use lib "lib";
+use RDF::TrineShortcuts qw[:all];
+use Data::Dumper;
+
+my $data = rdf_parse(<<'TURTLE', type=>'turtle');
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+
+<http://tobyinkster.co.uk/#i>
+	foaf:name "Toby Inkster" ;
+	foaf:page [ foaf:name "Toby Inkster" ] ;
+	foaf:junk "Foo <ex xmlns=\"http://example.com/junk\">Bar</ex>"^^rdf:XMLLiteral ;
+	foaf:mbox <mailto:tobyink@cpan.org> .
+TURTLE
+
+print Dumper(flatten_iterator(rdf_query(<<'SPARQL', $data), literal_as=>'ntriples'));
+PREFIX foaf: <http://xmlns.com/foaf/0.1/>
+PREFIX util: <http://buzzword.org.uk/2011/functions/util#>
+PREFIX junk: <http://example.com/junk> 
+SELECT
+	?name
+	(util:uc(?name) AS ?ucname)
+	(util:trim(util:sprintf("   Je m'apelle %s   "@fr, ?name)) AS ?intro)
+	(util:skolem(?page, "oid") AS ?skolempage)
+	(util:preg_replace("t", "x", ?name, "ig") AS ?mangled)
+	(util:find_xpath("//junk:ex", ?junk, 0) AS ?found)
+WHERE
+{
+	?person foaf:name ?name ; foaf:page ?page ; foaf:junk ?junk.
+}
+SPARQL
+
+

lib/RDF/Query/Functions/Buzzword/Util.pm

+package RDF::Query::Functions::Buzzword::Util;
+
+our $VERSION = '0.001';
+
+use common::sense;
+use Data::UUID;
+use RDF::Query::Error qw(:try);
+use Scalar::Util qw(blessed reftype refaddr looks_like_number);
+use XML::LibXML;
+
+use constant OID_PREFIX => '1.3.6.1.4.1.33926.9';
+my $UUID_URI = 'http://buzzword.org.uk/2011/functions/util#_Data::UUID';
+
+sub install
+{
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#uc"} ||= sub {
+		my ($query, $node) = @_;
+		
+		unless (blessed($node) and $node->isa('RDF::Trine::Node::Literal'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "uc function requires literal argument");
+		}
+
+		return RDF::Query::Node::Literal->new(
+			uc( $node->literal_value ),
+			$node->literal_value_language,
+			$node->literal_datatype,
+			);
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#lc"} ||= sub {
+		my ($query, $node) = @_;
+		
+		unless (blessed($node) and $node->isa('RDF::Trine::Node::Literal'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "lc function requires literal argument");
+		}
+
+		return RDF::Query::Node::Literal->new(
+			lc( $node->literal_value ),
+			$node->literal_value_language,
+			$node->literal_datatype,
+			);
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#ltrim"} ||= sub {
+		my ($query, $node) = @_;
+		
+		unless (blessed($node) and $node->isa('RDF::Trine::Node::Literal'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "ltrim function requires literal argument");
+		}
+
+		(my $trimmed = $node->literal_value) =~ s/^\s+//;
+
+		return RDF::Query::Node::Literal->new(
+			$trimmed,
+			$node->literal_value_language,
+			$node->literal_datatype,
+			);
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#rtrim"} ||= sub {
+		my ($query, $node) = @_;
+		
+		unless (blessed($node) and $node->isa('RDF::Trine::Node::Literal'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "rtrim function requires literal argument");
+		}
+
+		(my $trimmed = $node->literal_value) =~ s/\s+$//;
+
+		return RDF::Query::Node::Literal->new(
+			$trimmed,
+			$node->literal_value_language,
+			$node->literal_datatype,
+			);
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#trim"} ||= sub {
+		my ($query, $node) = @_;
+		
+		unless (blessed($node) and $node->isa('RDF::Trine::Node::Literal'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "trim function requires literal argument");
+		}
+
+		(my $trimmed = $node->literal_value) =~ s/\s+$//;
+		$trimmed =~ s/^\s+//;
+
+		return RDF::Query::Node::Literal->new(
+			$trimmed,
+			$node->literal_value_language,
+			$node->literal_datatype,
+			);
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#sprintf"} ||= sub {
+		my ($query, $pattern, @nodes) = @_;
+		
+		unless (blessed($pattern) and $pattern->isa('RDF::Trine::Node::Literal'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "sprintf function requires first argument to be literal");
+		}
+
+		my @litnodes = map {
+			$_->is_literal ? $_->literal_value : ( $_->is_resource ? $_->uri : $_->as_ntriples )
+			} @nodes;
+
+		return RDF::Query::Node::Literal->new(
+			sprintf($pattern->literal_value, @litnodes),
+			$pattern->literal_value_language,
+			$pattern->literal_datatype,
+			);
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#rand"} ||= sub {
+		my ($query, $upper, $lower) = @_;
+		
+		$lower ||= RDF::Trine::Node::Literal->new(0, undef, $upper->literal_datatype);
+		
+		unless (blessed($lower) and $lower->isa('RDF::Trine::Node::Literal') and $lower->is_numeric_type)
+		{
+			throw RDF::Query::Error::TypeError(-text => "rand function requires numeric lower bound (e.g. xsd:integer)");
+		}
+
+		unless (blessed($upper) and $upper->isa('RDF::Trine::Node::Literal') and $upper->is_numeric_type)
+		{
+			throw RDF::Query::Error::TypeError(-text => "rand function requires numeric upper bound (e.g. xsd:integer)");
+		}
+
+		unless ($lower->literal_datatype eq $upper->literal_datatype)
+		{
+			throw RDF::Query::Error::TypeError(-text => "rand function requires lower and upper bound to be of same datatype");
+		}
+
+		my $rand = $lower->literal_value + rand( $upper->literal_value - $lower->literal_value );
+		if ($lower->literal_datatype =~ qr<^http://www.w3.org/2001/XMLSchema#(integer|non(Positive|Negative)Integer|(positive|negative)Integer|long|int|short|byte|unsigned(Long|Int|Short|Byte))$>)
+		{
+			$rand = int($rand);
+		}
+
+		return RDF::Query::Node::Literal->new(
+			$rand,
+			undef,
+			$lower->literal_datatype,
+			);
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#defragment"} ||= sub {
+		my ($query, $node) = @_;
+
+		unless (blessed($node) and $node->isa('RDF::Trine::Node::Resource'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "defragment function requires first argument to be IRI");
+		}
+
+		my ($defragged) = split /#/, $node->uri;
+
+		return RDF::Query::Node::Resource->new($defragged);
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#str_replace"} ||= sub {
+		my ($query, $pattern, $replacement, $node) = @_;
+
+		unless (blessed($pattern)     and $pattern->isa('RDF::Trine::Node::Literal')
+		and     blessed($replacement) and $replacement->isa('RDF::Trine::Node::Literal')
+		and     blessed($node)        and $node->isa('RDF::Trine::Node::Literal'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "str_replace function requires all three arguments to be literals");
+		}
+
+		$pattern     = $pattern->literal_value;
+		$replacement = $replacement->literal_value;
+		
+		my $new_value = $node->literal_value;
+		$new_value =~ s/\Q$pattern\E/$replacement/g;
+
+		return RDF::Query::Node::Literal->new(
+			$new_value,
+			$node->literal_value_language,
+			$node->literal_datatype,
+			);
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#preg_replace"} ||= sub {
+		my ($query, $pattern, $replacement, $node, $mode) = @_;
+
+		$mode ||= RDF::Trine::Node::Literal->new('g');
+
+		unless (blessed($pattern)     and $pattern->isa('RDF::Trine::Node::Literal')
+		and     blessed($replacement) and $replacement->isa('RDF::Trine::Node::Literal')
+		and     blessed($node)        and $node->isa('RDF::Trine::Node::Literal')
+		and     blessed($mode)        and $mode->isa('RDF::Trine::Node::Literal'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "preg_replace function requires all four arguments to be literals");
+		}
+		
+		$pattern     = $pattern->literal_value;
+		$replacement = $replacement->literal_value;
+		$mode        = $mode->literal_value;
+		
+		throw RDF::Query::Error::FilterEvaluationError(-text => "preg_replace function passed an invalid mode")
+			unless $mode =~ /^[msixpogc]+$/;
+		
+		my $new_value = $node->literal_value;
+		eval '$new_value =~ s/$pattern/$replacement/'.$mode.';';
+
+		return RDF::Query::Node::Literal->new(
+			$new_value,
+			$node->literal_value_language,
+			$node->literal_datatype,
+			);
+	};
+
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#uuid"} ||= sub {
+		my ($query) = @_;
+		
+		$query->{_query_cache}{$UUID_URI} ||= Data::UUID->new;
+		my $uuid = $query->{_query_cache}{$UUID_URI};
+		
+		return RDF::Query::Node::Literal->new($uuid->create_str);
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#uuid_uri"} ||= sub {
+		my ($query) = @_;
+		
+		$query->{_query_cache}{$UUID_URI} ||= Data::UUID->new;
+		my $uuid = $query->{_query_cache}{$UUID_URI};
+		
+		return RDF::Query::Node::Resource->new('urn:uuid:' . $uuid->create_str);
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#oid"} ||= sub {
+		my ($query) = @_;
+		
+		$query->{_query_cache}{$UUID_URI} ||= Data::UUID->new;
+		my $uuid = $query->{_query_cache}{$UUID_URI};
+		
+		my $hex = $uuid->create_hex;
+		$hex =~ s/^0x//i;
+		
+		my $oid = OID_PREFIX;
+		while ($hex)
+		{
+			my $chunk = substr($hex, 0, 4);
+			$hex = substr($hex, 4);
+			$oid .= "." . hex($chunk);
+		}
+		
+		return RDF::Query::Node::Literal->new($oid);
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#oid_uri"} ||= sub {
+		my ($query) = @_;
+		
+		$query->{_query_cache}{$UUID_URI} ||= Data::UUID->new;
+		my $uuid = $query->{_query_cache}{$UUID_URI};
+		
+		my $hex = $uuid->create_hex;
+		$hex =~ s/^0x//i;
+		
+		my $oid = OID_PREFIX;
+		while ($hex)
+		{
+			my $chunk = substr($hex, 0, 4);
+			$hex = substr($hex, 4);
+			$oid .= "." . hex($chunk);
+		}
+		
+		return RDF::Query::Node::Resource->new('urn:oid:' . $oid);
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#skolem"} ||= sub {
+		my ($query, $node, $scheme) = @_;
+		$scheme ||= RDF::Trine::Node::Literal->new('UUID');
+
+		unless (blessed($node) and $node->isa('RDF::Trine::Node::Blank'))
+		{
+			return $node;
+		}
+		
+		my $storage = "http://buzzword.org.uk/2011/functions/util#_SKOLEM";
+		$query->{_query_cache}{$storage}{$node->blank_identifier} = do
+		{
+			if ($scheme->literal_value =~ /^oid$/i)
+			{
+				$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#oid_uri"}->($query);
+			}
+			elsif ($scheme->literal_value =~ /^uuid$/i)
+			{
+				$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#uuid_uri"}->($query);
+			}
+			else
+			{
+				throw RDF::Query::Error::FilterEvaluationError(-text => "Only skolemisation schemes supported are OID and UUID.");
+			}
+		};
+		
+		return $query->{_query_cache}{$storage}{$node->blank_identifier};
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/util#find_xpath"} ||= sub {
+		my ($query, $path, $xmllit, $index) = @_;
+		$index ||= RDF::Trine::Node::Literal->new(0, undef, 'http://www.w3.org/2001/XMLSchema#integer');
+
+		unless (blessed($path) and $path->isa('RDF::Trine::Node::Literal'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "find_xpath function requires first argument to be literal");
+		}
+
+		unless (blessed($xmllit) and $xmllit->isa('RDF::Trine::Node::Literal') and $xmllit->literal_datatype eq 'http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral')
+		{
+			throw RDF::Query::Error::TypeError(-text => "find_xpath function requires second argument to be an rdf:XMLLiteral");
+		}
+
+		unless (blessed($index) and $index->isa('RDF::Trine::Node::Literal') and $index->is_numeric_type and int($index->literal_value) eq $index->literal_value)
+		{
+			throw RDF::Query::Error::TypeError(-text => "find_xpath function requires first argument to be an xsd:integer");
+		}
+
+		my $doc = '<rdf:XMLLiteral xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'
+			. $xmllit->literal_value
+			. "</rdf:XMLLiteral>";
+		my $dom      = XML::LibXML->new->parse_string($doc);
+		my $context  = XML::LibXML::XPathContext->new($dom);
+		
+		my $mappings = $query->{parsed}{namespaces};  # undocumented!
+		while (my ($prefix, $namespace_uri) = each %$mappings)
+		{
+			$context->registerNs($prefix, $namespace_uri)
+				unless defined $context->lookupNs($prefix);
+		}
+		my @nodes = $context->findnodes($path->literal_value);
+		if (defined $nodes[ $index->literal_value ])
+		{
+			return RDF::Query::Node::Literal->new(
+				$nodes[ $index->literal_value ]->toString,
+				undef,
+				'http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral',
+				);
+		}
+		else
+		{
+			return undef;
+		}
+	};
+} #/sub install
+
+1;
+
+__END__
+
+=head1 NAME
+
+RDF::Query::Functions::Buzzword::Util - plugin for buzzword.org.uk utility functions
+
+=head1 SYNOPSIS
+
+  use RDF::TrineShortcuts qw[:all];
+  use Data::Dumper;
+  
+  my $data = rdf_parse(<<'TURTLE', type=>'turtle');
+  @prefix foaf: <http://xmlns.com/foaf/0.1/> .
+  @prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+  
+  <http://tobyinkster.co.uk/#i>
+    foaf:name "Toby Inkster" ;
+    foaf:page [ foaf:name "Toby Inkster" ] ;
+    foaf:junk "Foo <ex xmlns=\"urn:junk\">Bar</ex>"^^rdf:XMLLiteral ;
+    foaf:mbox <mailto:tobyink@cpan.org> .
+  TURTLE
+  
+  $r = rdf_query(<<'SPARQL', $data);
+  PREFIX foaf: <http://xmlns.com/foaf/0.1/>
+  PREFIX util: <http://buzzword.org.uk/2011/functions/util#>
+  PREFIX junk: <urn:junk> 
+  SELECT
+    ?name
+    (util:uc(?name) AS ?ucname)
+    (util:trim(util:sprintf(" I am %s "@en, ?name)) AS ?intro)
+    (util:skolem(?page, "oid") AS ?skolempage)
+    (util:preg_replace("t", "x", ?name, "ig") AS ?mangled)
+    (util:find_xpath("//junk:ex", ?junk, 0) AS ?found)
+  WHERE
+  {
+    ?person foaf:name ?name ; foaf:page ?page ; foaf:junk ?junk.
+  }
+  SPARQL
+  
+  print Dumper(flatten_iterator($r, literal_as=>'ntriples'));
+
+=head1 DESCRIPTION
+
+This is a plugin for RDF::Query providing a number of extension functions.
+
+=over
+
+=item * http://buzzword.org.uk/2011/functions/util#defragment
+
+=item * http://buzzword.org.uk/2011/functions/util#find_xpath
+
+=item * http://buzzword.org.uk/2011/functions/util#lc
+
+=item * http://buzzword.org.uk/2011/functions/util#ltrim
+
+=item * http://buzzword.org.uk/2011/functions/util#oid
+
+=item * http://buzzword.org.uk/2011/functions/util#oid_uri
+
+=item * http://buzzword.org.uk/2011/functions/util#preg_replace
+
+=item * http://buzzword.org.uk/2011/functions/util#rand
+
+=item * http://buzzword.org.uk/2011/functions/util#rtrim
+
+=item * http://buzzword.org.uk/2011/functions/util#skolem
+
+=item * http://buzzword.org.uk/2011/functions/util#sprintf
+
+=item * http://buzzword.org.uk/2011/functions/util#str_replace
+
+=item * http://buzzword.org.uk/2011/functions/util#trim
+
+=item * http://buzzword.org.uk/2011/functions/util#uc
+
+=item * http://buzzword.org.uk/2011/functions/util#uuid
+
+=item * http://buzzword.org.uk/2011/functions/util#uuid_uri
+
+=back
+
+=head1 SEE ALSO
+
+L<RDF::Query>.
+
+L<http://perlrdf.org/>.
+
+=head1 AUTHOR
+
+Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
+
+=head1 COPYRIGHT
+
+Copyright 2010-2011 Toby Inkster
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+use lib 'inc';
+use Test::More tests => 1;
+use Test::Signature;
+signature_ok();
+use Test::More tests => 1;
+use RDF::Query;
+
+ok(
+	defined $RDF::Query::functions{'http://buzzword.org.uk/2011/functions/util#uuid'},
+	"Module::Pluggable found me!",
+	);