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

Last change on this file since 35 was 35, checked in by ktk, 14 years ago

rdfint

  • implemented set method
  • Property svn:eol-style set to native
File size: 37.5 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 $newTriples = \ARC2::getTriplesFromIndex( $index);
476
477 } else {
478
479 // load RDF data from URI
480 $uri = $toparse;
481 $parseSource = "ARC parser";
482
483 $debugMessage = "Parsing RDF data from: $uri\n";
484 $this->debugger->sendMessage( $debugMessage, self::debugcontext);
485
486 $arcParser = \ARC2::getRDFParser();
487 $arcParser->parse( $uri);
488 $newTriples = $arcParser->getTriples();
489
490 }
491 // could something be parsed ?
492 $result = (count( $newTriples) > 0);
493
494 // determine operation mode
495 $fMerge = (count( $this->aTriples) != 0);
496 if ($fMerge)
497 $operation = "merged";
498 else
499 $operation = "loaded";
500
501 // merge new triples into existing data
502 $this->_addTriples( $newTriples);
503
504 $debugMessage = count( $newTriples) . " triple(s) $operation from $parseSource\n";
505 foreach ($newTriples as $aTriple) {
506 $debugMessage .= $this->_debug_formatArcTriple( $aTriple)."\n";
507 }
508 if ($fMerge)
509 $debugMessage .= "\n" . count( $this->aTriples) . " triples total\n";
510 $this->debugger->sendMessage( $debugMessage, self::debugcontext);
511
512 return $result;
513
514 } // public function parse
515
516 // --------------------------------------------------------
517
518 /**
519 * \name Basic API
520 */
521 /**@{ */ /***************** DOXYGEN GROUP START - Basic APIs */
522
523
524 /**
525 * Retrieves a list of all values expressed in the RDF Data that match the given subject and property.
526 *
527 * \param property Property that the subject should be linked with
528 * \param value Value that the specified property should have
529 * \retval array List of subjects
530 * \retval boolean false: the graph contains no data
531 *
532 * If no arguments are provided, all values from within the RDF data are returned.
533 */
534 public function getSubjects( $property = Null, $value = NULL) {
535
536 $predicate = $property;
537 $object = $value;
538 $aTriplesResult = $this->_filterTriples( NULL, $predicate, $object,
539 $debugmessage_filterresult);
540 // build result
541 $aresult = false;
542 foreach ($aTriplesResult as $aTriple) {
543 $aresult[]= $this->_shrink( $aTriple[ 's']);
544 }
545
546 // make result entries unique
547 if ($aresult !== false)
548 $aresult = array_values( array_unique( $aresult));
549
550 // build debug message
551 if (!$this->_calledFromOwnCode()) {
552 $formattedTriple = $this->_debug_formatTripleFromParms( NULL, $predicate, $object);
553 if (!$aresult)
554 $debugmessage = "No subjects found for: $formattedTriple\n";
555 else {
556 $debugmessage = count( $aresult) . " subjects found";
557 if ($formattedTriple != '')
558 $debugmessage .= " for: $formattedTriple";
559 $debugmessage .= "\n";
560 foreach( $aresult as $result) {
561 $debugmessage .= "$result\n";
562 }
563 }
564 $this->debugger->sendMessage( "$debugmessage\n$debugmessage_filterresult",
565 self::debugcontext);
566
567 } // if (!$this->_calledFromOwnCode())
568
569 return $aresult;
570
571 } // public function getSubjects
572
573 // --------------------------------------------------------
574
575 /**
576 * Retrieves a list of all properties expressed in the RDF data that match the given subject.
577 *
578 * \param subject Subject to be searched
579 * \retval array List of properties
580 * \retval boolean false: the subject was not found
581 *
582 * If a subject is not provided, all properties expressed in the RDF data are returned.
583 */
584 public function getProperties( $subject = Null) {
585
586 $aTriplesResult = $this->_filterTriples( $subject, Null, Null,
587 $debugmessage_filterresult);
588
589 // build result
590 $aresult = false;
591 foreach ($aTriplesResult as $aTriple) {
592 $aresult[]= $this->_shrink( $aTriple[ 'p']);
593 }
594
595 // make result entries unique
596 if ($aresult !== false)
597 $aresult = array_values( array_unique( $aresult));
598
599 // build debug message
600 if (!$this->_calledFromOwnCode()) {
601 if (!$aresult) {
602 if ( $subject = Null)
603 $debugmessage = "No properties found\n";
604 else
605 $debugmessage = "No properties found for subject: $subject\n";
606 }
607 else {
608
609 $debugmessage = count( $aresult) . " properties found for: $subject\n";
610 foreach( $aresult as $result) {
611 $debugmessage .= "$result\n";
612 }
613 }
614 $this->debugger->sendMessage( "$debugmessage\n$debugmessage_filterresult",
615 self::debugcontext);
616
617 } // if (!$this->_calledFromOwnCode())
618
619 return $aresult;
620
621
622 } // public function getProperties
623
624 // --------------------------------------------------------
625
626 /**
627 * Gets an associative list of unique properties with their values
628 * expressed in the RDF data that match the given subject.
629 *
630 * \param subject Subject to be searched
631 * \retval array Associative list of unqique properties and their values
632 * \retval boolean false: the subject could not be found
633 *
634 * If a subject isn't provided, all unique properties expressed in the RDF data are returned.
635 *
636 * \note
637 * - All non-unique properties are \c NOT returned !
638 * - This method is a library specific extension to the RDF API and RDFa API
639 */
640 public function _getUniqueProperties( $subject = Null) {
641
642 $aTriplesResult = $this->_filterTriples( $subject, Null, Null,
643 $debugmessage_filterresult);
644
645 // build result
646 $aresult = false;
647 $afound = array();
648 foreach ($aTriplesResult as $pos => $aTriple) {
649 // isolate predicate and check if it was already found
650 $thispredicate = $this->_shrink( $aTriple[ 'p']);
651 if (isset( $afound[ $thispredicate])) {
652 unset( $aresult[ $thispredicate]);
653 continue;
654 } else {
655 $afound[ $thispredicate] = true;
656 $aresult[ $thispredicate] = $this->_shrink( $aTriple[ 'o']);
657 }
658 }
659
660 // build debug message
661 if (!$this->_calledFromOwnCode()) {
662 if (!$aresult) {
663 if ( $subject = Null)
664 $debugmessage = "No unique properties found\n";
665 else
666 $debugmessage = "No unique properties found for subject: $subject\n";
667 }
668 else {
669
670 $debugmessage = count( $aresult) . " unique properties found for: $subject\n";
671 foreach( $aresult as $result) {
672 $debugmessage .= "$result\n";
673 }
674 }
675 $this->debugger->sendMessage( "$debugmessage\n$debugmessage_filterresult",
676 self::debugcontext);
677
678 } // if (!$this->_calledFromOwnCode())
679
680 return $aresult;
681
682
683 } // public function _getUniqueProperties
684
685 // --------------------------------------------------------
686
687 /**
688 * Retrieves a list of all values expressed in the RDF data that match the given subject and property.
689 *
690 * \param subject Subject to be searched
691 * \param property Property to be searched
692 * \retval array List of values
693 * \retval boolean false: a matching subject was not found
694 *
695 * If no arguments are provided, all values expressed in the RDF data are returned.
696 */
697 public function getValues( $subject = Null, $property = Null) {
698
699 $predicate = $property;
700 $aTriplesResult = $this->_filterTriples( $subject, $predicate, Null,
701 $debugmessage_filterresult);
702
703 // build result
704 $aresult = false;
705 foreach ($aTriplesResult as $aTriple) {
706 $aresult[]= $this->_shrink( $aTriple[ 'o']);
707 }
708
709 // make result entries unique
710 if ($aresult !== false)
711 $aresult = array_values( array_unique( $aresult));
712
713 // build debug message
714 if (!$this->_calledFromOwnCode()) {
715 $formattedTriple = $this->_debug_formatTripleFromParms( $subject, $predicate, Null);
716 if ($aresult === false) {
717 if ( $subject = Null)
718 $debugmessage = "No values found\n";
719 else
720 $debugmessage = "No values found for subject: $formattedTriple\n";
721 }
722 else {
723
724 $debugmessage = count( $aresult) . " values found for: $formattedTriple\n";
725 foreach( $aresult as $result) {
726 $debugmessage .= "$result\n";
727 }
728 }
729 $this->debugger->sendMessage( "$debugmessage\n$debugmessage_filterresult",
730 self::debugcontext);
731
732 } // if (!$this->_calledFromOwnCode())
733
734 return $aresult;
735
736 } // public function getValues
737
738 // --------------------------------------------------------
739
740 /**
741 * Retrieves the first available value expressed in the RDF data that matches the given subject and property.
742 *
743 * \param subject Subject to be searched
744 * \param property Property to be searched
745 * \retval string First value of the property
746 * \retval boolean false: the subject could not be found
747 *
748 * If no arguments are provided, the first value expressed in the RDF data is returned.
749 *
750 * \note This method is a library specific extension to the RDF API and RDFa API
751 */
752 public function _getFirstValue( $subject = Null, $property = Null) {
753
754 $predicate = $property;
755 $aTriplesResult = $this->_filterTriples( $subject, $predicate, Null,
756 $debugmessage_filterresult);
757
758 // build result
759 $result = false;
760 foreach ($aTriplesResult as $aTriple) {
761 $result = $this->_shrink( $aTriple[ 'o']);
762 break;
763 }
764
765 // build debug message
766 if (!$this->_calledFromOwnCode()) {
767 $formattedTriple = $this->_debug_formatTripleFromParms( $subject, $predicate, Null);
768 if (!$result) {
769 if ($subject == Null)
770 $debugmessage = "No value found\n";
771 else
772 $debugmessage = "No value found for: $formattedTriple\n";
773 }
774 else {
775 $debugmessage = "Value $result found for: $formattedTriple\n";
776 $triplecount = count( $aTriplesResult);
777 if ($triplecount == 1)
778 $debugmessage .= "One value available";
779 else
780 $debugmessage .= "First of $triplecount values available";
781 }
782 $this->debugger->sendMessage( "$debugmessage\n$debugmessage_filterresult",
783 self::debugcontext);
784
785 } // if (!$this->_calledFromOwnCode())
786
787 return $result;
788
789 } // public function _getFirstValue
790
791 // --------------------------------------------------------
792
793 /**
794 * Sets a property value for a subject.
795 *
796 * \param subject Subject to get the property set as URI or CURIE
797 * \param property Property to be added as URI or CURIE
798 * \param value Value to be set for the property as URI, CURIE or literal value
799 * \param type The type of the value, either 'uri', 'literal' or 'bnode'
800 * (case-insensitive). Specifying the first character is sufficient.
801 * If not specified, strings starting with 'http://' are taken as a URI,
802 * strings starting with '_:' as a blank node, otherwise as a literal
803 * \retval boolean true: property value set \n
804 * false: subject not found or invalid value type specified
805 *
806 * \see rdfa::Projection::set
807 *
808 * \note This method is a library specific extension to the RDF API and RDFa API
809 */
810
811 public function _setValue( $subject, $property, $value, $type = NULL) {
812
813 $predicate = $property;
814 $object = $value;
815
816 // resolve namespace prefixes
817 $uriSubject = $this->_resolve( $subject);
818 $uriPredicate = $this->_resolve( $predicate);
819 $uriObject = $this->_resolve( $object);
820
821 // resolve method may return NULL, then use original value
822 $uriSubject = ($uriSubject === NULL) ? $subject : $uriSubject;
823 $uriPredicate = ($uriPredicate === NULL) ? $predicate : $uriPredicate;
824 $uriObject = ($uriObject === NULL) ? $object : $uriObject;
825
826 // check what type we have
827 // This is not clear in the API. It could be interpreted that the only acceptable value is a literal
828 // We have to check what the intention is and adjust it accordingly or enhance the specs
829 if ($type == null) {
830 if (preg_match('#^http://.?#', $value))
831 $type = 'uri';
832 elseif (preg_match('#/^_:.?/#', $value))
833 $type = 'bnode';
834 else
835 $type = 'literal';
836 }
837
838 if (!preg_match('#^(uri|bnode|literal)$#', strtolower($type)))
839 return false;
840
841 // construct the aTriple array
842 $aTriple = array();
843 $aTriple['type'] = 'triple';
844 $aTriple['s'] = $uriSubject;
845 $aTriple['p'] = $uriPredicate;
846 $aTriple['o'] = $uriObject;
847 $aTriple['s_type'] = 'uri';
848 $aTriple['p_type'] = 'uri';
849 $aTriple['o_type'] = strtolower($type);
850
851 $aTriple['o_datatype'] = ''; // data types, currently not supported by the API
852 $aTriple['o_lang'] = ''; // language, same same
853
854
855 $this->_addTriple( $aTriple);
856
857 return true;
858
859 } // public function _setValue
860
861 /**@} */ /***************** DOXYGEN GROUP ENDS - Basic APIs */
862
863 // --------------------------------------------------------
864
865 /**
866 * \name Projection API
867 */
868 /**@{ */ /***************** DOXYGEN GROUP START - Projection APIs */
869
870 /**
871 * Retrieves a single Projection given a subject and an optional template.
872 * A template can be provided for the purpose of building the Projection in an application-specific way.
873 *
874 * \param subject Subject to be searched
875 * \param template Associative array( URI/CURIE => membername) as a template to be applied to the projection object
876 * \retval rdfa::Projection The projection on the subject
877 * \retval boolean false: the subject was not found
878 *
879 */
880 public function getProjection( $subject, $template = Null) {
881
882 if ($subject == Null)
883 return false;
884
885 if ((!$template == Null) && (!is_array( $template))) {
886 $this->debugger->sendError( "Invalid type specified as template (must be array) " . get_class() ."::getProjection", self::debugcontext);
887 return false;
888 }
889
890 $fLogMessages = (!$this->_calledFromOwnCode());
891 if (! $this->_subjectExists( $subject)) {
892echo "### no subject \n";
893 if ($fLogMessages) $this->debugger->sendMessage( "Cannot get projection for subject: $subject",
894 self::debugcontext);
895 $result = false;
896 } else {
897 if ($fLogMessages) $this->debugger->sendMessage( "Get projection for subject: $subject",
898 self::debugcontext);
899 $result = new \rdfa\Projection( $this, $subject, $template);
900 }
901
902 return $result;
903
904 } // public function getProjection
905
906 // --------------------------------------------------------
907
908 /**
909 * Retrieves a list of Projections given an optional property and value to match against.
910 * A template can be provided for the purpose of building the Projection in an application-specific way.
911 *
912 * \param property Property that the subject of the projections should be linked with
913 * \param value Value that the specified property should have
914 * \param template Associative array( URI/CURIE => membername) as a template to be applied to the projection object
915 * \retval array List of rdfa::Projection as projections on the matching subjects
916 * \retval boolean false: a matching subject was not found
917 *
918 * If no arguments are provided, projections are created for all subjects from within the RDF data.
919 */
920 public function getProjections( $property = Null, $value = Null, $template = Null) {
921
922 if ((!$template == Null) && (!is_array( $template))) {
923 $this->debugger->sendError( "Invalid type specified as template (must be array) for " . get_class() ."::getProjections", self::debugcontext);
924 return false;
925 }
926
927 // providing log output about call
928 $predicate = $property;
929 $object = $value;
930 $fLogMessages = (!$this->_calledFromOwnCode());
931 $formattedTriple = $this->_debug_formatTripleFromParms( Null, $predicate, $object);
932 if ($formattedTriple != '')
933 $formattedTriple = " for: $formattedTriple";
934
935 $asubjects = $this->getSubjects( $property, $value);
936 if ($asubjects == false) {
937 if ($fLogMessages) $this->debugger->sendMessage( "Cannot get projections $formattedTriple",
938 self::debugcontext);
939 $aprojection = false;
940 } else {
941 $count = count( $asubjects);
942 if ($fLogMessages) $this->debugger->sendMessage( "Getting $count projections $formattedTriple",
943 self::debugcontext);
944 $aprojection = array();
945 foreach ($asubjects as $subject) {
946 $aprojection[] = new \rdfa\Projection( $this, $subject, $template);
947 }
948 }
949
950 return $aprojection;
951
952 } // public function getProjection
953
954 // --------------------------------------------------------
955
956 /**
957 * Retrieves a list of Projections based on a set of selection criteria.
958 * A template can be provided for the purpose of building the Projection in an application-specific way.
959 *
960 * \param query An associative array( URI/CURIE => value) specifying a multiple property filter
961 * \param template An associative array( URI/CURIE => membername) as a template to be applied to the projection object
962 * \retval array List of rdfa::Projection as projections on the matching subjects
963 * \retval boolean false: a matching subject was not found
964 *
965 * If no arguments are provided, projections are created for all subjects from within the RDF data.
966 */
967 public function query( $query, $template = Null) {
968
969 if ((!$query == Null) && (!is_array( $query))) {
970 $this->debugger->sendError( "Invalid type specified as query (must be array) for " . get_class() ."::query", self::debugcontext);
971 return false;
972 }
973 if (count( $query) == 0) {
974 $this->debugger->sendError( "Empty query array specified " . get_class() ."::query", self::debugcontext);
975 return false;
976 }
977
978 if ((!$template == Null) && (!is_array( $template))) {
979 $this->debugger->sendError( "Invalid type specified as template (must be array) " . get_class() ."::query", self::debugcontext);
980 return false;
981 }
982
983 // providing log output about query
984 $fLogMessages = (!$this->_calledFromOwnCode());
985 $debugmessage = "Querying for projections\n";
986 foreach ($query as $property => $value) {
987 $debugmessage .= "filter: $property $value\n";
988 }
989 if ($fLogMessages) $this->debugger->sendMessage( "$debugmessage",
990 self::debugcontext);
991
992 // do initial search
993 list( $property, $value) = each( $query);
994 $asubjects = $this->getSubjects( $property, $value);
995 if ($asubjects == false)
996 return false;
997
998 // create projections for examination
999 $aprojection_test = array();
1000 $count = count( $asubjects);
1001 if ($fLogMessages) $this->debugger->sendMessage( "Getting $count projections for filter test",
1002 self::debugcontext);
1003 foreach ($asubjects as $subject) {
1004 $aprojection_test[] = new \rdfa\Projection( $this, $subject, $template);
1005 $debugmessage .= "$subject\n";
1006 }
1007
1008 // determine which projections have to be filtered out
1009 $aFilteredSubjects = array();
1010 foreach ($aprojection_test as $projection) {
1011 $subject = $projection->getSubject();
1012 foreach ($query as $property => $value) {
1013 $avalues = $projection->getAll( $property);
1014 if ($avalues == false) {
1015 // filter this projection: property not found
1016 $aFilteredSubjects[ $subject] = "Property $property not found";
1017 break;
1018 } else {
1019 if (array_search( $value, $avalues, true) === false) {
1020 // filter this projection: specific value not found
1021 $aFilteredSubjects[ $subject] = "Property $property does not have value: $value";
1022 break;
1023 }
1024 }
1025 }
1026 }
1027 $count = count( $aFilteredSubjects);
1028 if ($count == 0) {
1029 $debugmessage = "No projections filtered\n";
1030 } else {
1031 $debugmessage = "Filtering $count projections\n";
1032 foreach ( $aFilteredSubjects as $subject => $reason) {
1033 $debugmessage .= "$subject: $reason\n";
1034 }
1035 }
1036
1037 if ($fLogMessages) $this->debugger->sendMessage( "$debugmessage",
1038 self::debugcontext);
1039
1040 // take over unfiltered projections
1041 $aprojection = array();
1042 foreach ($aprojection_test as $projection) {
1043 if (array_key_exists( $projection->getSubject(), $aFilteredSubjects) === false) {
1044 $aprojection[] = $projection;
1045 } else {
1046 unset( $projection);
1047 }
1048 }
1049
1050 // create log output
1051 $count = count( $aprojection);
1052 $debugmessage = "Returning $count projections \n";
1053 foreach ($aprojection as $projection) {
1054 $debugmessage .= "{$projection->getSubject()}\n";
1055 }
1056 if ($fLogMessages) $this->debugger->sendMessage( "$debugmessage",
1057 self::debugcontext);
1058 return $aprojection;
1059
1060 } // public function query
1061
1062 /**@} */ /***************** DOXYGEN GROUP ENDS - Projection APIs */
1063
1064 /**
1065 * Returns a serialization of the triple data.
1066 *
1067 * \param type Supported RDF serialization MIME Media Types according to http://www.iana.org/assignments/media-types/index.html
1068 * \retval string Serialization of the graph data
1069 * \retval boolean false: the serialization type is invalid
1070 *
1071 * Currently supported mime type identifiers are:
1072 *
1073 * - application/rdf+xml
1074 * - application/rdfxml
1075 * - text/turtle
1076 * - application/x-turtle
1077 * - text/n3
1078 * - application/json
1079 *
1080 * In addition to those, the internal type identifiers of this library can be used as well:
1081 * - rdfxml
1082 * - turtle
1083 * - n3
1084 * - json
1085 *
1086 * \note This method is a library specific extension to the RDF API and RDFa API
1087 */
1088 public function _serialize( $type ) {
1089 // set the supported types and shortcut them to the ones we use in there
1090 $validTypes = array( 'application/rdf+xml' => 'rdfxml' ,
1091 'application/rdfxml' => 'rdfxml',
1092 'text/turtle' => 'turtle',
1093 'application/x-turtle' => 'turtle',
1094 'text/n3' => 'n3',
1095 'application/json' => 'json');
1096
1097 // check if explicit mime type is specified
1098 if (isset( $validTypes[ $type])) {
1099 $type = $validTypes[ $type];
1100 } else {
1101 // check if internal type is given
1102 $internalTypes = array_unique( array_flip( $validTypes));
1103 if (!isset( $internalTypes[ $type]))
1104 return false;
1105 }
1106
1107 switch ($type) {
1108
1109 case 'rdfxml':
1110 $ser = \ARC2::getRDFXMLSerializer();
1111 break;
1112
1113 case 'turtle':
1114 $ser = \ARC2::getTurtleSerializer();
1115 break;
1116
1117 case 'n3':
1118 $ser = \ARC2::getNTriplesSerializer();
1119 break;
1120
1121 case 'json':
1122 $ser = \ARC2::getRDFJSONSerializer();
1123 break;
1124
1125 } // switch ($type)
1126
1127 return $ser->getSerializedTriples( $this->aTriples);
1128
1129 } // public function _serialize
1130
1131
1132} // class Data
1133
1134?>
Note: See TracBrowser for help on using the repository browser.