8 * Copyright (c) 2009-2013 Nicholas J Humfrey. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 * 3. The name of the author 'Nicholas J Humfrey" may be used to endorse or
18 * promote products derived from this software without specific prior
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
34 * @copyright Copyright (c) 2009-2013 Nicholas J Humfrey
35 * @license http://www.opensource.org/licenses/bsd-license.php
39 * Class to serialise an EasyRdf_Graph to Turtle
40 * with no external dependancies.
42 * http://www.w3.org/TR/turtle/
45 * @copyright Copyright (c) 2009-2013 Nicholas J Humfrey
46 * @license http://www.opensource.org/licenses/bsd-license.php
48 class EasyRdf_Serialiser_Turtle extends EasyRdf_Serialiser
50 private $outputtedBnodes = array();
53 * Given a IRI string, escape and enclose in angle brackets.
55 * @param string $resourceIri
58 public static function escapeIri($resourceIri)
60 $escapedIri = str_replace('>', '\\>', $resourceIri);
61 return "<$escapedIri>";
65 * Given a string, enclose in quotes and escape any quotes in the string.
66 * Strings containing tabs, linefeeds or carriage returns will be
67 * enclosed in three double quotes (""").
69 * @param string $value
72 public static function quotedString($value)
74 if (preg_match('/[\t\n\r]/', $value)) {
75 $escaped = str_replace(array('\\', '"""'), array('\\\\', '\\"""'), $value);
76 return '"""'.$escaped.'"""';
78 $escaped = str_replace(array('\\', '"'), array('\\\\', '\\"'), $value);
79 return '"'.$escaped.'"';
84 * Given a an EasyRdf_Resource or URI, convert it into a string, suitable to
85 * be written to a Turtle document. URIs will be shortened into CURIES
88 * @param EasyRdf_Resource $resource The resource to convert to a Turtle string
89 * @param boolean $createNamespace If true, a new namespace may be created
92 public function serialiseResource($resource, $createNamespace = false)
94 if (is_object($resource)) {
95 if ($resource->isBNode()) {
96 return $resource->getUri();
99 $resource = $resource->getUri();
102 $short = EasyRdf_Namespace::shorten($resource, $createNamespace);
105 $this->addPrefix($short);
109 return self::escapeIri($resource);
113 * Given an EasyRdf_Literal object, convert it into a string, suitable to
114 * be written to a Turtle document. Supports multiline literals and literals with
115 * datatypes or languages.
117 * @param EasyRdf_Literal $literal
120 public function serialiseLiteral($literal)
122 $value = strval($literal);
123 $quoted = self::quotedString($value);
125 if ($datatype = $literal->getDatatypeUri()) {
126 if ($datatype == 'http://www.w3.org/2001/XMLSchema#integer') {
127 return sprintf('%d', $value);
128 } elseif ($datatype == 'http://www.w3.org/2001/XMLSchema#decimal') {
129 return sprintf('%s', $value);
130 } elseif ($datatype == 'http://www.w3.org/2001/XMLSchema#double') {
131 return sprintf('%e', $value);
132 } elseif ($datatype == 'http://www.w3.org/2001/XMLSchema#boolean') {
133 return sprintf('%s', $value);
135 $escaped = $this->serialiseResource($datatype, true);
136 return sprintf('%s^^%s', $quoted, $escaped);
138 } elseif ($lang = $literal->getLang()) {
139 return $quoted . '@' . $lang;
146 * Convert an EasyRdf object into a string suitable to
147 * be written to a Turtle document.
149 * @param EasyRdf_Resource|EasyRdf_Literal $object
152 public function serialiseObject($object)
154 if ($object instanceof EasyRdf_Resource) {
155 return $this->serialiseResource($object);
156 } elseif ($object instanceof EasyRdf_Literal) {
157 return $this->serialiseLiteral($object);
159 throw new InvalidArgumentException(
160 "serialiseObject() requires \$object to be ".
161 "of type EasyRdf_Resource or EasyRdf_Literal"
168 * Protected method to serialise a RDF collection
171 protected function serialiseCollection($node, $indent)
176 if ($id = $node->getBNodeId()) {
177 $this->outputtedBnodes[$id] = true;
180 $value = $node->get('rdf:first');
181 $node = $node->get('rdf:rest');
182 if ($node and $node->hasProperty('rdf:first')) {
186 if ($value !== null) {
187 $serialised = $this->serialiseObject($value);
189 $turtle .= "\n$indent $serialised";
191 $turtle .= " ".$serialised;
196 $turtle .= "\n$indent)";
204 * Protected method to serialise the properties of a resource
207 protected function serialiseProperties($res, $depth = 1)
209 $properties = $res->propertyUris();
210 $indent = str_repeat(' ', ($depth*2)-1);
213 if (count($properties) > 1) {
214 $turtle .= "\n$indent";
218 foreach ($properties as $property) {
219 if ($property === 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type') {
222 $pStr = $this->serialiseResource($property, true);
226 $turtle .= " ;\n$indent";
229 $turtle .= ' ' . $pStr;
232 foreach ($res->all("<$property>") as $object) {
237 if ($object instanceof EasyRdf_Collection) {
238 $turtle .= ' ' . $this->serialiseCollection($object, $indent);
239 } elseif ($object instanceof EasyRdf_Resource and $object->isBNode()) {
240 $id = $object->getBNodeId();
241 $rpcount = $this->reversePropertyCount($object);
242 if ($rpcount <= 1 and !isset($this->outputtedBnodes[$id])) {
243 // Nested unlabelled Blank Node
244 $this->outputtedBnodes[$id] = true;
246 $turtle .= $this->serialiseProperties($object, $depth+1);
249 // Multiple properties pointing to this blank node
250 $turtle .= ' ' . $this->serialiseObject($object);
253 $turtle .= ' ' . $this->serialiseObject($object);
265 } elseif ($pCount > 1) {
266 $turtle .= "\n" . str_repeat(' ', (($depth-1)*2)-1);
275 protected function serialisePrefixes()
278 foreach ($this->prefixes as $prefix => $count) {
279 $url = EasyRdf_Namespace::get($prefix);
280 $turtle .= "@prefix $prefix: <$url> .\n";
288 protected function serialiseSubjects(EasyRdf_Graph $graph, $filterType)
291 foreach ($graph->resources() as $resource) {
292 /** @var $resource EasyRdf_Resource */
293 // If the resource has no properties - don't serialise it
294 $properties = $resource->propertyUris();
295 if (count($properties) == 0) {
299 // Is this node of the right type?
300 $thisType = $resource->isBNode() ? 'bnode' : 'uri';
301 if ($thisType != $filterType) {
305 if ($thisType == 'bnode') {
306 $id = $resource->getBNodeId();
308 if (isset($this->outputtedBnodes[$id])) {
309 // Already been serialised
313 $this->outputtedBnodes[$id] = true;
314 $rpcount = $this->reversePropertyCount($resource);
319 $turtle .= $this->serialiseResource($resource);
322 $turtle .= $this->serialiseResource($resource);
325 $turtle .= $this->serialiseProperties($resource);
332 * Serialise an EasyRdf_Graph to Turtle.
334 * @param EasyRdf_Graph $graph An EasyRdf_Graph object.
335 * @param string $format The name of the format to convert to.
336 * @param array $options
337 * @throws EasyRdf_Exception
338 * @return string The RDF in the new desired format.
340 public function serialise($graph, $format, array $options = array())
342 parent::checkSerialiseParams($graph, $format);
344 if ($format != 'turtle' and $format != 'n3') {
345 throw new EasyRdf_Exception(
346 "EasyRdf_Serialiser_Turtle does not support: $format"
350 $this->prefixes = array();
351 $this->outputtedBnodes = array();
354 $turtle .= $this->serialiseSubjects($graph, 'uri');
355 $turtle .= $this->serialiseSubjects($graph, 'bnode');
357 if (count($this->prefixes)) {
358 return $this->serialisePrefixes() . "\n" . $turtle;