source: php/trunk/classes/rdfa_Data.php@ 70

Last change on this file since 70 was 69, checked in by cla, 14 years ago

rdfint

  • added code to parse() to check for non-successful SQL query
  • parse() now returns false if no new triples added
  • Property svn:eol-style set to native
File size: 37.3 KB
Line 
1<?php
2
3/* RDFInt.php - RDF Interfaces for PHP
4 * Copyright 2011 netlabs.org
5 * Author: Christian Langanke, Adrian Gschwend
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20namespace rdfa;
21
22/**
23 * \class Data
24 * \brief This class implements a RDF data class as described in the
25 * \c RDF \c API and \c RDFa \c API specs of W3C.
26 * \note Non-standard methods are marked as library specific extensions
27 * and the methodname is prepended by a single underscore character.
28 * For further information on differences to the W3C specifications,
29 * see the section
30 * \htmllink{page_w3cspecs.html,W3C Specifications Overview and Library Compatibility}
31 * \author Christian Langanke
32 * \author Adrian Gschwend
33 * \date 2011
34 */
35
36/*
37 * This class internally uses the ARC triple array format for storing RDF data:
38 *
39 * Array
40 * (
41 * [type] => --- 'triple' --- (others ???)
42 * [s] => --- uri ---
43 * [p] => --- uri ---
44 * [o] => --- uri or bnode or literal ---
45 * [s_type] => --- 'uri' or 'bnode' ---
46 * [p_type] => --- 'uri' --- (others ???)
47 * [o_type] => => --- 'uri' or 'bnode' oder 'literal' ---
48 * [o_datatype] => --- datatype or empty ---
49 * [o_lang] => --- lang code or empty ---
50 * )
51 *
52 */
53
54class Data {
55
56 /**
57 * Version of the class
58 */
59 const version = '1.0.0';
60 /**
61 * Name of the fDebug context
62 */
63 const debugcontext = 'RDFA_DATA';
64
65 private $debugger;
66 private $aTriples;
67 private $aNamespace;
68
69 // ---------------------------------------
70
71 /**
72 * Creates a data class instance.
73 * In order to access data, you have to call rdfa::Data::parse().
74 */
75 public function __construct() {
76
77 // setup debugger
78 $this->debugger = \fDebug::getInstance();
79
80 // initialize some vars
81 $this->aTriples = array();
82 $this->aNamespace = array();
83
84 } // public function __construct
85
86 // ########################################################
87 // private interface
88 // ########################################################
89
90 // debug formatting helpers
91
92 /**
93 * Return string with debug output, representing data from an ARC triple array.
94 * Values not being speciefied (==Null) are not included in the text.
95 * The object or value is enclosed in double quotes, if it is a literal
96 */
97 private function _debug_formatArcTriple( $aTriple) {
98 $subject = $aTriple[ 's'];
99 $predicate = $aTriple[ 'p'];
100 $object = $aTriple[ 'o'];
101 $objecttype = (isset( $aTriple[ 'o_type'])) ? $aTriple[ 'o_type'] : '';
102
103 $litSubject = ($subject == NULL) ? "" : $this->_shrinkFormatted( $subject);
104 $litPredicate = ($predicate == NULL) ? "" : $this->_shrinkFormatted( $predicate);
105
106 if ($object == NULL)
107 $litObject = "";
108 else {
109 if ($objecttype == 'literal')
110 $litObject = '"' . $object . '"';
111 else
112 $litObject = $this->_shrinkFormatted( $object);
113 }
114
115 $result = trim( "$litSubject $litPredicate $litObject");
116 return $result;
117 }
118
119 /**
120 * Return string with debug output, representing data the specified values.
121 * Values not being speciefied (==Null) are not included in the text.
122 * The object or value is enclosed in double quotes, if it is a literal
123 */
124 private function _debug_formatTripleFromParms( $subject, $predicate, $object) {
125 return $this->_debug_formatArcTriple( array( 's' => $subject,
126 'p' => $predicate,
127 'o' => $object));
128 }
129
130 // --------------------------------------------------------
131
132 /**
133 * Filters out requested triples by specified subject/predicate/object
134 * Returns array of matching triples in ARC triple array format
135 */
136 private function _filterTriples( $subject = NULL,
137 $predicate = NULL,
138 $object = NULL,
139 &$debugmessage_result) {
140
141 $searchTriple = array ( 's' => $subject, 'p' => $predicate, 'o' => $object);
142
143 // resolve namespace prefixes
144 $uriSubject = $this->_resolve( $subject);
145 $uriPredicate = $this->_resolve( $predicate);
146 $uriObject = $this->_resolve( $object);
147
148 // resolve method may return NULL, then use original value
149 $uriSubject = ($uriSubject === NULL) ? $subject : $uriSubject;
150 $uriPredicate = ($uriPredicate === NULL) ? $predicate : $uriPredicate;
151 $uriObject = ($uriObject === NULL) ? $object : $uriObject;
152
153 // filter all available triples
154 $aresult = array();
155 foreach ($this->aTriples as $aTriple) {
156
157 // must be triple
158 if ((isset( $aTriple[ 'type'])) &&
159 ($aTriple[ 'type'] != 'triple'))
160 continue;
161
162 // check subject and predicate match if given
163 if (($uriSubject != NULL) && (strcmp( $uriSubject, $aTriple[ 's'])))
164 continue;
165
166 if (($uriPredicate != NULL) && (strcmp( $uriPredicate, $aTriple[ 'p'])))
167 continue;
168
169 // check object if given
170 if ($uriObject != NULL) {
171
172 // check object match
173 if (strcmp( $uriObject, $aTriple[ 'o']))
174 continue;
175
176 } // if ($uriObject != NULL) {
177
178 // store in result array
179 $aresult[] = $aTriple;
180
181 } // foreach ($aTriples as $aTriple)
182
183 // show result in debugger
184 $triplecount = count( $this->aTriples);
185 $resultcount = count( $aresult);
186 if ($resultcount == 0)
187 $debugmessage_result = "No match in $triplecount triples!";
188 else {
189 $debugmessage_result = "Matches in $triplecount triples: \n";
190 foreach ($aresult as $aTriple) {
191 $debugmessage_result .= $this->_debug_formatArcTriple( $aTriple)."\n";
192 }
193 }
194
195 return $aresult;
196
197 } // private function _filterTriples
198
199 // --------------------------------------------------------
200
201 /**
202 * Checks if a given subject exists in the RDF data.
203 */
204 private function _subjectExists( $subject) {
205
206 $uriSubject = $this->_shrink( $subject);
207 foreach ($this->aTriples as $aTriple) {
208 if ( $uriSubject == $this->_shrink( $aTriple[ 's']))
209 return true;
210 }
211
212 return false;
213
214 } // private function _subjectExists
215
216 // --------------------------------------------------------
217
218 /**
219 * Add mapping to internal namespace list.
220 * This method is called internally, not producing debug output.
221 */
222 private function _addNamespaceMapping( $prefix, $uriNamespace) {
223
224 if ($prefix == '')
225 return false;
226
227 // add colon to prefix as internally req.
228 if (substr( $prefix, -1) != ':')
229 $prefix = "$prefix:";
230
231 if (isset( $this->aNamespace[ $prefix])) {
232 $oldUriNamespace = $this->aNamespace[ $prefix];
233 if ($oldUriNamespace == $uriNamespace)
234 $debugmessage = "Namespace mapping exists: @prefix $prefix <$uriNamespace> .";
235 else
236 $debugmessage = "Namespace mapping overwritten: @prefix $prefix <$uriNamespace> .\n" .
237 "Old mapping was: @prefix $prefix <$oldUriNamespace>";
238 } else {
239 $debugmessage = "Namespace mapping added: @prefix $prefix <$uriNamespace> .";
240 }
241
242 // set URI
243 $this->aNamespace[ $prefix] = $uriNamespace;
244 return $debugmessage;
245
246 } // private function _addNamespaceMapping
247
248 // --------------------------------------------------------
249
250 /**
251 * Shrink URI to CURI, but return <URI> if mapping not successful.
252 */
253 private function _shrinkFormatted( $uri) {
254
255 $curie = $this->_shrink( $uri);
256 if ($curie == $uri)
257 $curie = "<$uri>";
258 return $curie;
259
260 } // private function _shrinkFormatted
261
262 // --------------------------------------------------------
263
264 // CURRENTLY NOT FULLY IMPLEMENTED
265
266 // adjust blank node IDs so that same blank nodes of a
267 // triple sets has the same id as in the currently contained
268 // triple set.
269 // NOTE: a blank nodes may not be adjusted if there are
270 // two different values for the same blank node in the two
271 // triple sets, as then the two blank nodes may represent a
272 // multivalue property
273
274 private function _adjustBlankNodes( $aTriplesToAdd) {
275
276
277// foreach ($this->aTriples as $aTriple) {
278// if ($aTriple[ 'o_type'] == 'bnode')
279// echo $aTriple[ 'o']."\n";
280// }
281
282
283 return $aTriplesToAdd;
284
285 }
286
287 // --------------------------------------------------------
288
289 // add an array of ARC triple arrays
290 // to the data hold by the instance
291 private function _addTriples( $aTriples) {
292 if (!is_array( $aTriples))
293 return;
294
295 // adjust blank node IDs first
296 $aTripleAdusted = $this->_adjustBlankNodes( $aTriples);
297
298 // add adjusted triples
299 foreach ($aTripleAdusted as $aTriple) {
300 $this->_addTriple( $aTriple);
301 }
302 } // private function _addTriples
303
304 // --------------------------------------------------------
305
306 // add an ARC triple array
307 // to the data hold by the instance
308 // the triple is only merged if not yet included !
309 private function _addTriple( $aTriple) {
310
311
312 if (!is_array( $aTriple))
313 return;
314 if ((isset( $aTriple['type']) &&
315 ($aTriple['type'] != 'triple')))
316 return;
317
318 // if triple is not contained, add it!
319 if (array_search( $aTriple, $this->aTriples) === false)
320 $this->aTriples[] = $aTriple;
321
322 } // private function _addTriple
323
324 // --------------------------------------------------------
325
326 /**
327 * Checks if callers caller is from own library code.
328 * Helps to reduce unwanted ddebug output
329 */
330 private function _calledFromOwnCode() {
331 // check for own namespace in backtrace
332 $trace = array_slice(debug_backtrace( false), 2, 1);
333 $class = (isset( $trace[0]['class'])) ? $trace[0]['class'] : '';
334 return (strpos( $class, 'rdfa\\') === 0);
335 } // private function _calledFromOwnCode
336
337 // ########################################################
338 // public interface
339 // ########################################################
340
341 /**
342 * \name Mapping API
343 */
344 /**@{ */ /***************** DOXYGEN GROUP START - Mapping APIs */
345
346 /**
347 * Sets short-hand IRI mappings that are used by the API to map URIs to CURIEs.
348 *
349 * \param prefix Namespace prefix of the mapping
350 * \param uriNamespace Namespace base URI of the mapping
351 * \retval void
352 *
353 * \note The return value
354 * - is not described in the \c RDFa \c API specification
355 * - should be a string in the \c RDF \c API, but the value is not described
356 */
357 public function setMapping( $prefix, $uriNamespace) {
358 $debugmessage = $this->_addNamespaceMapping( $prefix, $uriNamespace);
359 if ($debugmessage != false)
360 $this->debugger->sendMessage( $debugmessage,
361 self::debugcontext);
362 return;
363 } // public function setMapping
364
365 // --------------------------------------------------------
366
367 /**
368 * Resolves a CURI to a URI.
369 *
370 * If the prefix is not known then this method will return null.
371 *
372 * \param curie CURIE to be resolved to a URI
373 * \retval string URI
374 * \retval NULL Mapping could not be found
375 *
376 * \note This method is a library specific extension to the RDF API and RDFa API
377 */
378 public function _resolve( $curie) {
379
380 $replacecount = 1;
381 $uri = NULL;
382 if ($curie != NULL) {
383 if ($this->aNamespace != NULL) {
384 if ((strpos( $uri, ":/") !== false) ||
385 (strpos( $uri, "_:") === 0)) {
386 $uri = $curi;
387 } else {
388 // check for namespaces
389 foreach ($this->aNamespace as $prefix => $uriNamespace) {
390 // check for prefix match
391 $posPrefix = strpos( $curie, $prefix);
392 if ($posPrefix === 0) {
393 // replace prefix and bail out
394 $uri = str_replace( $prefix, $uriNamespace, $curie, $replacecount);
395 break;
396 }
397 } // foreach ($this->aNamespace
398 } // if ((strpos ...
399 } // if ($aNamespace != NULL) {
400 } // if ($uri != NULL) {
401
402 return $uri;
403
404 } // public function _resolve
405
406 // --------------------------------------------------------
407
408 /**
409 * Shrinks a URI to a CURIE.
410 *
411 * If no mapping exists for the given URI, the URI is returned.
412 *
413 * \param uri URI to be shrinked to a CURIE
414 * \retval string CURIE or URI
415 *
416 * \note This method is a library specific extension to the RDF API and RDFa API
417 */
418 public function _shrink( $uri) {
419
420 $replacecount = 1;
421 if ($uri != NULL) {
422 if ($this->aNamespace != NULL) {
423 if (strpos( $uri, ":/") !== false) {
424 foreach ($this->aNamespace as $prefix => $uriNamespace) {
425 // search namespace URI
426 $posNamespace = strpos( $uri, $uriNamespace);
427 if ($posNamespace === false)
428 continue;
429 // replace namespace URI and bail out
430 $uri = str_replace( $uriNamespace, $prefix, $uri, &$replacecount);
431 break;
432 } // foreach ($aNamespace
433 } // if (strpos( $uri, ":/") !== false)
434 } // if ($aNamespace != NULL) {
435 } // if ($uri != NULL) {
436
437 return $uri;
438
439 } // public function _shrink
440
441 /**@} */ /***************** DOXYGEN GROUP ENDS - Mapping APIs */
442
443 // --------------------------------------------------------
444
445 /**
446 * Parses RDF data from a URI.
447 *
448 * \param toparse Resource to parse from. This can be a URI or an object of SparqlQuery
449 * \retval boolean true: the parsed graph contains data
450 * \retval boolean false: the parsed graph contains no data
451 *
452 * \note
453 * - This method is defined for the RDF API, but a library specific extension to the RDFa API.
454 * - Parsing from a SPARQL CONSTRUCT query by using a SparqlQuery object, is a library specific
455 * extension to both the RDF API and RDFa API.
456 * - On all consecutive calls to parse, the newly parsed graph is merged into the existing graph.
457 * Such a feature is not described in the W3C specs
458 */
459 public function parse( $toparse) {
460
461 // check type to parse
462 if ((is_object( $toparse)) &&
463 ('\\' . get_class( $toparse) == '\\rdfa\\SparqlQuery')) {
464
465 // receive RDF data from raw SPARQL query
466 $query = $toparse;
467 $parseSource = "SPARQL query";
468
469 $statement = $query->getStatement();
470 $debugMessage = "Parsing RDF data from SPARQL query\n" .
471 "Executing statement: \n" . $statement;
472 $this->debugger->sendMessage( $debugMessage, self::debugcontext);
473
474 $index = $query->run();
475 if ($index !== false)
476 $newTriples = \ARC2::getTriplesFromIndex( $index);
477 else
478 $newTriples = array();
479
480 } else {
481
482 // load RDF data from URI
483 $uri = $toparse;
484 $parseSource = "ARC parser";
485
486 $debugMessage = "Parsing RDF data from: $uri\n";
487 $this->debugger->sendMessage( $debugMessage, self::debugcontext);
488
489 $arcParser = \ARC2::getRDFParser();
490 $arcParser->parse( $uri);
491 $newTriples = $arcParser->getTriples();
492 }
493 // could something be parsed ?
494 $result = (count( $newTriples) > 0);
495
496 // determine operation mode
497 $fMerge = (count( $this->aTriples) != 0);
498 if ($fMerge)
499 $operation = "merged";
500 else
501 $operation = "loaded";
502
503 // merge new triples into existing data
504 $this->_addTriples( $newTriples);
505
506 $debugMessage = count( $newTriples) . " triple(s) $operation from $parseSource\n";
507 foreach ($newTriples as $aTriple) {
508 $debugMessage .= $this->_debug_formatArcTriple( $aTriple)."\n";
509 }
510 if ($fMerge)
511 $debugMessage .= "\n" . count( $this->aTriples) . " triples total\n";
512 $this->debugger->sendMessage( $debugMessage, self::debugcontext);
513
514 return $result;
515
516 } // public function parse
517
518 // --------------------------------------------------------
519
520 /**
521 * \name Basic API
522 */
523 /**@{ */ /***************** DOXYGEN GROUP START - Basic APIs */
524
525
526 /**
527 * Retrieves a list of all values expressed in the RDF Data that match the given subject and property.
528 *
529 * \param property Property that the subject should be linked with
530 * \param value Value that the specified property should have
531 * \retval array List of subjects
532 * \retval boolean false: the graph contains no data
533 *
534 * If no arguments are provided, all values from within the RDF data are returned.
535 */
536 public function getSubjects( $property = Null, $value = NULL) {
537
538 $predicate = $property;
539 $object = $value;
540 $aTriplesResult = $this->_filterTriples( NULL, $predicate, $object,
541 $debugmessage_filterresult);
542 // build result
543 $aresult = false;
544 foreach ($aTriplesResult as $aTriple) {
545 $aresult[]= $this->_shrink( $aTriple[ 's']);
546 }
547
548 // make result entries unique
549 if ($aresult !== false)
550 $aresult = array_values( array_unique( $aresult));
551
552 // build debug message
553 if (!$this->_calledFromOwnCode()) {
554 $formattedTriple = $this->_debug_formatTripleFromParms( NULL, $predicate, $object);
555 if (!$aresult)
556 $debugmessage = "No subjects found for: $formattedTriple\n";
557 else {
558 $debugmessage = count( $aresult) . " subjects found";
559 if ($formattedTriple != '')
560 $debugmessage .= " for: $formattedTriple";
561 $debugmessage .= "\n";
562 foreach( $aresult as $result) {
563 $debugmessage .= "$result\n";
564 }
565 }
566 $this->debugger->sendMessage( "$debugmessage\n$debugmessage_filterresult",
567 self::debugcontext);
568
569 } // if (!$this->_calledFromOwnCode())
570
571 return $aresult;
572
573 } // public function getSubjects
574
575 // --------------------------------------------------------
576
577 /**
578 * Retrieves a list of all properties expressed in the RDF data that match the given subject.
579 *
580 * \param subject Subject to be searched
581 * \retval array List of properties
582 * \retval boolean false: the subject was not found
583 *
584 * If a subject is not provided, all properties expressed in the RDF data are returned.
585 */
586 public function getProperties( $subject = Null) {
587
588 $aTriplesResult = $this->_filterTriples( $subject, Null, Null,
589 $debugmessage_filterresult);
590
591 // build result
592 $aresult = false;
593 foreach ($aTriplesResult as $aTriple) {
594 $aresult[]= $this->_shrink( $aTriple[ 'p']);
595 }
596
597 // make result entries unique
598 if ($aresult !== false)
599 $aresult = array_values( array_unique( $aresult));
600
601 // build debug message
602 if (!$this->_calledFromOwnCode()) {
603 if (!$aresult) {
604 if ( $subject = Null)
605 $debugmessage = "No properties found\n";
606 else
607 $debugmessage = "No properties found for subject: $subject\n";
608 }
609 else {
610
611 $debugmessage = count( $aresult) . " properties found for: $subject\n";
612 foreach( $aresult as $result) {
613 $debugmessage .= "$result\n";
614 }
615 }
616 $this->debugger->sendMessage( "$debugmessage\n$debugmessage_filterresult",
617 self::debugcontext);
618
619 } // if (!$this->_calledFromOwnCode())
620
621 return $aresult;
622
623
624 } // public function getProperties
625
626 // --------------------------------------------------------
627
628 /**
629 * Gets an associative list of unique properties with their values
630 * expressed in the RDF data that match the given subject.
631 *
632 * \param subject Subject to be searched
633 * \retval array Associative list of unqique properties and their values
634 * \retval boolean false: the subject could not be found
635 *
636 * If a subject isn't provided, all unique properties expressed in the RDF data are returned.
637 *
638 * \note
639 * - All non-unique properties are \c NOT returned !
640 * - This method is a library specific extension to the RDF API and RDFa API
641 */
642 public function _getUniqueProperties( $subject = Null) {
643
644 $aTriplesResult = $this->_filterTriples( $subject, Null, Null,
645 $debugmessage_filterresult);
646
647 // build result
648 $aresult = false;
649 $afound = array();
650 foreach ($aTriplesResult as $pos => $aTriple) {
651 // isolate predicate and check if it was already found
652 $thispredicate = $this->_shrink( $aTriple[ 'p']);
653 if (isset( $afound[ $thispredicate])) {
654 unset( $aresult[ $thispredicate]);
655 continue;
656 } else {
657 $afound[ $thispredicate] = true;
658 $aresult[ $thispredicate] = $this->_shrink( $aTriple[ 'o']);
659 }
660 }
661
662 // build debug message
663 if (!$this->_calledFromOwnCode()) {
664 if (!$aresult) {
665 if ( $subject = Null)
666 $debugmessage = "No unique properties found\n";
667 else
668 $debugmessage = "No unique properties found for subject: $subject\n";
669 }
670 else {
671
672 $debugmessage = count( $aresult) . " unique properties found for: $subject\n";
673 foreach( $aresult as $result) {
674 $debugmessage .= "$result\n";
675 }
676 }
677 $this->debugger->sendMessage( "$debugmessage\n$debugmessage_filterresult",
678 self::debugcontext);
679
680 } // if (!$this->_calledFromOwnCode())
681
682 return $aresult;
683
684
685 } // public function _getUniqueProperties
686
687 // --------------------------------------------------------
688
689 /**
690 * Retrieves a list of all values expressed in the RDF data that match the given subject and property.
691 *
692 * \param subject Subject to be searched
693 * \param property Property to be searched
694 * \retval array List of values
695 * \retval boolean false: a matching subject was not found
696 *
697 * If no arguments are provided, all values expressed in the RDF data are returned.
698 */
699 public function getValues( $subject = Null, $property = Null) {
700
701 $predicate = $property;
702 $aTriplesResult = $this->_filterTriples( $subject, $predicate, Null,
703 $debugmessage_filterresult);
704
705 // build result
706 $aresult = false;
707 foreach ($aTriplesResult as $aTriple) {
708 $aresult[]= $this->_shrink( $aTriple[ 'o']);
709 }
710
711 // make result entries unique
712 if ($aresult !== false)
713 $aresult = array_values( array_unique( $aresult));
714
715 // build debug message
716 if (!$this->_calledFromOwnCode()) {
717 $formattedTriple = $this->_debug_formatTripleFromParms( $subject, $predicate, Null);
718 if ($aresult === false) {
719 if ( $subject = Null)
720 $debugmessage = "No values found\n";
721 else
722 $debugmessage = "No values found for subject: $formattedTriple\n";
723 }
724 else {
725
726 $debugmessage = count( $aresult) . " values found for: $formattedTriple\n";
727 foreach( $aresult as $result) {
728 $debugmessage .= "$result\n";
729 }
730 }
731 $this->debugger->sendMessage( "$debugmessage\n$debugmessage_filterresult",
732 self::debugcontext);
733
734 } // if (!$this->_calledFromOwnCode())
735
736 return $aresult;
737
738 } // public function getValues
739
740 // --------------------------------------------------------
741
742 /**
743 * Retrieves the first available value expressed in the RDF data that matches the given subject and property.
744 *
745 * \param subject Subject to be searched
746 * \param property Property to be searched
747 * \retval string First value of the property
748 * \retval boolean false: the subject could not be found
749 *
750 * If no arguments are provided, the first value expressed in the RDF data is returned.
751 *
752 * \note This method is a library specific extension to the RDF API and RDFa API
753 */
754 public function _getFirstValue( $subject = Null, $property = Null) {
755
756 $predicate = $property;
757 $aTriplesResult = $this->_filterTriples( $subject, $predicate, Null,
758 $debugmessage_filterresult);
759
760 // build result
761 $result = false;
762 foreach ($aTriplesResult as $aTriple) {
763 $result = $this->_shrink( $aTriple[ 'o']);
764 break;
765 }
766
767 // build debug message
768 if (!$this->_calledFromOwnCode()) {
769 $formattedTriple = $this->_debug_formatTripleFromParms( $subject, $predicate, Null);
770 if (!$result) {
771 if ($subject == Null)
772 $debugmessage = "No value found\n";
773 else
774 $debugmessage = "No value found for: $formattedTriple\n";
775 }
776 else {
777 $debugmessage = "Value $result found for: $formattedTriple\n";
778 $triplecount = count( $aTriplesResult);
779 if ($triplecount == 1)
780 $debugmessage .= "One value available";
781 else
782 $debugmessage .= "First of $triplecount values available";
783 }
784 $this->debugger->sendMessage( "$debugmessage\n$debugmessage_filterresult",
785 self::debugcontext);
786
787 } // if (!$this->_calledFromOwnCode())
788
789 return $result;
790
791 } // public function _getFirstValue
792
793 // --------------------------------------------------------
794
795 /**
796 * Sets a property value for a subject.
797 *
798 * \param subject Subject to get the property set as URI or CURIE
799 * \param property Property to be added as URI or CURIE
800 * \param value Value to be set for the property as URI, CURIE or literal value
801 * \param type The type of the value, either 'uri', 'literal' or 'bnode'
802 * (case-insensitive). Specifying the first character is sufficient.
803 * If not specified, strings starting with 'http://' are taken as a URI,
804 * strings starting with '_:' as a blank node, otherwise as a literal
805 * \retval boolean true: property value set \n
806 * false: subject not found or invalid value type specified
807 *
808 * \see rdfa::Projection::set
809 *
810 * \note This method is a library specific extension to the RDF API and RDFa API
811 */
812
813 public function _setValue( $subject, $property, $value, $type = NULL) {
814
815 $predicate = $property;
816 $object = $value;
817
818 // resolve namespace prefixes
819 $uriSubject = $this->_resolve( $subject);
820 $uriPredicate = $this->_resolve( $predicate);
821 $uriObject = $this->_resolve( $object);
822
823 // resolve method may return NULL, then use original value
824 $uriSubject = ($uriSubject === NULL) ? $subject : $uriSubject;
825 $uriPredicate = ($uriPredicate === NULL) ? $predicate : $uriPredicate;
826 $uriObject = ($uriObject === NULL) ? $object : $uriObject;
827
828 // check what type we have
829 // This is not clear in the API. It could be interpreted that the only acceptable value is a literal
830 // We have to check what the intention is and adjust it accordingly or enhance the specs
831 if ($type == null) {
832 if (preg_match('#^http://.?#', $value))
833 $type = 'uri';
834 elseif (preg_match('#/^_:.?/#', $value))
835 $type = 'bnode';
836 else
837 $type = 'literal';
838 }
839
840 if (!preg_match('#^(uri|bnode|literal)$#', strtolower($type)))
841 return false;
842
843 // construct the aTriple array
844 $aTriple = array();
845 $aTriple['type'] = 'triple';
846 $aTriple['s'] = $uriSubject;
847 $aTriple['p'] = $uriPredicate;
848 $aTriple['o'] = $uriObject;
849 $aTriple['s_type'] = 'uri';
850 $aTriple['p_type'] = 'uri';
851 $aTriple['o_type'] = strtolower($type);
852
853 $aTriple['o_datatype'] = ''; // data types, currently not supported by the API
854 $aTriple['o_lang'] = ''; // language, same same
855
856
857 $this->_addTriple( $aTriple);
858
859 return true;
860
861 } // public function _setValue
862
863 /**@} */ /***************** DOXYGEN GROUP ENDS - Basic APIs */
864
865 // --------------------------------------------------------
866
867 /**
868 * \name Projection API
869 */
870 /**@{ */ /***************** DOXYGEN GROUP START - Projection APIs */
871
872 /**
873 * Retrieves a single Projection given a subject and an optional template.
874 * A template can be provided for the purpose of building the Projection in an application-specific way.
875 *
876 * \param subject Subject to be searched
877 * \param template Associative array( URI/CURIE => membername) as a template to be applied to the projection object
878 * \retval rdfa::Projection The projection on the subject
879 * \retval boolean false: the subject was not found
880 *
881 */
882 public function getProjection( $subject, $template = Null) {
883
884 if ($subject == Null)
885 return false;
886
887 if ((!$template == Null) && (!is_array( $template))) {
888 $this->debugger->sendError( "Invalid type specified as template (must be array) " . get_class() ."::getProjection", self::debugcontext);
889 return false;
890 }
891
892 $fLogMessages = (!$this->_calledFromOwnCode());
893 if (! $this->_subjectExists( $subject)) {
894 if ($fLogMessages) $this->debugger->sendMessage( "Cannot get projection for subject: $subject",
895 self::debugcontext);
896 $result = false;
897 } else {
898 if ($fLogMessages) $this->debugger->sendMessage( "Get projection for subject: $subject",
899 self::debugcontext);
900 $result = new \rdfa\Projection( $this, $subject, $template);
901 }
902
903 return $result;
904
905 } // public function getProjection
906
907 // --------------------------------------------------------
908
909 /**
910 * Retrieves a list of Projections given an optional property and value to match against.
911 * A template can be provided for the purpose of building the Projection in an application-specific way.
912 *
913 * \param property Property that the subject of the projections should be linked with
914 * \param value Value that the specified property should have
915 * \param template Associative array( URI/CURIE => membername) as a template to be applied to the projection object
916 * \retval array List of rdfa::Projection as projections on the matching subjects
917 * \retval boolean false: a matching subject was not found
918 *
919 * If no arguments are provided, projections are created for all subjects from within the RDF data.
920 */
921 public function getProjections( $property = Null, $value = Null, $template = Null) {
922
923 if ((!$template == Null) && (!is_array( $template))) {
924 $this->debugger->sendError( "Invalid type specified as template (must be array) for " . get_class() ."::getProjections", self::debugcontext);
925 return false;
926 }
927
928 // providing log output about call
929 $predicate = $property;
930 $object = $value;
931 $fLogMessages = (!$this->_calledFromOwnCode());
932 $formattedTriple = $this->_debug_formatTripleFromParms( Null, $predicate, $object);
933 if ($formattedTriple != '')
934 $formattedTriple = " for: $formattedTriple";
935
936 $asubjects = $this->getSubjects( $property, $value);
937 if ($asubjects == false) {
938 if ($fLogMessages) $this->debugger->sendMessage( "Cannot get projections $formattedTriple",
939 self::debugcontext);
940 $aprojection = false;
941 } else {
942 $count = count( $asubjects);
943 if ($fLogMessages) $this->debugger->sendMessage( "Getting $count projections $formattedTriple",
944 self::debugcontext);
945 $aprojection = array();
946 foreach ($asubjects as $subject) {
947 $aprojection[] = new \rdfa\Projection( $this, $subject, $template);
948 }
949 }
950
951 return $aprojection;
952
953 } // public function getProjection
954
955 // --------------------------------------------------------
956
957 /**
958 * Retrieves a list of Projections based on a set of selection criteria.
959 * A template can be provided for the purpose of building the Projection in an application-specific way.
960 *
961 * \param query An associative array( URI/CURIE => value) specifying a multiple property filter
962 * \param template An associative array( URI/CURIE => membername) as a template to be applied to the projection object
963 * \retval array List of rdfa::Projection as projections on the matching subjects
964 * \retval boolean false: a matching subject was not found
965 *
966 * If no arguments are provided, projections are created for all subjects from within the RDF data.
967 */
968 public function query( $query, $template = Null) {
969
970 if ((!$query == Null) && (!is_array( $query))) {
971 $this->debugger->sendError( "Invalid type specified as query (must be array) for " . get_class() ."::query", self::debugcontext);
972 return false;
973 }
974 if (count( $query) == 0) {
975 $this->debugger->sendError( "Empty query array specified " . get_class() ."::query", self::debugcontext);
976 return false;
977 }
978
979 if ((!$template == Null) && (!is_array( $template))) {
980 $this->debugger->sendError( "Invalid type specified as template (must be array) " . get_class() ."::query", self::debugcontext);
981 return false;
982 }
983
984 // providing log output about query
985 $fLogMessages = (!$this->_calledFromOwnCode());
986 $debugmessage = "Querying for projections\n";
987 foreach ($query as $property => $value) {
988 $debugmessage .= "filter: $property $value\n";
989 }
990 if ($fLogMessages) $this->debugger->sendMessage( "$debugmessage",
991 self::debugcontext);
992
993 // do initial search
994 list( $property, $value) = each( $query);
995 $asubjects = $this->getSubjects( $property, $value);
996 if ($asubjects == false)
997 return false;
998
999 // examine properties
1000 $aprojection_test = array();
1001 $count = count( $asubjects);
1002 if ($fLogMessages) $this->debugger->sendMessage( "Testing $count subjects",
1003 self::debugcontext);
1004 $aFilteredSubjects = array();
1005 foreach ($asubjects as $subject) {
1006 $debugmessage .= "$subject\n";
1007 foreach ($query as $property => $value) {
1008 $avalues = $this->getValues( $subject, $property);
1009 if ($avalues == false) {
1010 // filter this subject: property not found
1011 $aFilteredSubjects[ $subject] = "Property $property not found";
1012 break;
1013 } else {
1014 if (array_search( $value, $avalues, true) === false) {
1015 // filter this projection: specific value not found
1016 $aFilteredSubjects[ $subject] = "Property $property does not have value: $value";
1017 break;
1018 }
1019 }
1020 }
1021 }
1022
1023 $count = count( $aFilteredSubjects);
1024 if ($count == 0) {
1025 $debugmessage = "No projections filtered\n";
1026 } else {
1027 $debugmessage = "Filtering $count subjects\n";
1028 foreach ( $aFilteredSubjects as $subject => $reason) {
1029 $debugmessage .= "$subject: $reason\n";
1030 }
1031 }
1032
1033 if ($fLogMessages) $this->debugger->sendMessage( "$debugmessage",
1034 self::debugcontext);
1035
1036 // take over unfiltered projections
1037 $aprojection = array();
1038 foreach ($asubjects as $subject) {
1039 if (array_key_exists( $subject, $aFilteredSubjects) === false)
1040 $aprojection[] = new \rdfa\Projection( $this, $subject, $template);
1041 }
1042
1043 // create projections on unfiltered subjects
1044 foreach ($aprojection as $projection) {
1045 $debugmessage .= "{$projection->getSubject()}\n";
1046 }
1047 $count = count( $aprojection);
1048 $debugmessage = "Returning $count projections \n";
1049
1050 if ($fLogMessages) $this->debugger->sendMessage( "$debugmessage",
1051 self::debugcontext);
1052 return $aprojection;
1053
1054 } // public function query
1055
1056 /**@} */ /***************** DOXYGEN GROUP ENDS - Projection APIs */
1057
1058 /**
1059 * Returns a serialization of the triple data.
1060 *
1061 * \param type Supported RDF serialization MIME Media Types according to http://www.iana.org/assignments/media-types/index.html
1062 * \retval string Serialization of the graph data
1063 * \retval boolean false: the serialization type is invalid
1064 *
1065 * Currently supported mime type identifiers are:
1066 *
1067 * - application/rdf+xml
1068 * - application/rdfxml
1069 * - text/turtle
1070 * - application/x-turtle
1071 * - text/n3
1072 * - application/json
1073 *
1074 * In addition to those, the internal type identifiers of this library can be used as well:
1075 * - rdfxml
1076 * - turtle
1077 * - n3
1078 * - json
1079 *
1080 * \note This method is a library specific extension to the RDF API and RDFa API
1081 */
1082 public function _serialize( $type ) {
1083 // set the supported types and shortcut them to the ones we use in there
1084 $validTypes = array( 'application/rdf+xml' => 'rdfxml' ,
1085 'application/rdfxml' => 'rdfxml',
1086 'text/turtle' => 'turtle',
1087 'application/x-turtle' => 'turtle',
1088 'text/n3' => 'n3',
1089 'application/json' => 'json');
1090
1091 // check if explicit mime type is specified
1092 if (isset( $validTypes[ $type])) {
1093 $type = $validTypes[ $type];
1094 } else {
1095 // check if internal type is given
1096 $internalTypes = array_unique( array_flip( $validTypes));
1097 if (!isset( $internalTypes[ $type]))
1098 return false;
1099 }
1100
1101 switch ($type) {
1102
1103 case 'rdfxml':
1104 $ser = \ARC2::getRDFXMLSerializer();
1105 break;
1106
1107 case 'turtle':
1108 $ser = \ARC2::getTurtleSerializer();
1109 break;
1110
1111 case 'n3':
1112 $ser = \ARC2::getNTriplesSerializer();
1113 break;
1114
1115 case 'json':
1116 $ser = \ARC2::getRDFJSONSerializer();
1117 break;
1118
1119 } // switch ($type)
1120
1121 return $ser->getSerializedTriples( $this->aTriples);
1122
1123 } // public function _serialize
1124
1125
1126} // class Data
1127
1128?>
Note: See TracBrowser for help on using the repository browser.