PageRenderTime 54ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/s3db3.5.10/pearlib/rdfapi-php/api/model/MemModel.php

https://code.google.com/p/s3db/
PHP | 1470 lines | 734 code | 195 blank | 541 comment | 168 complexity | 5d0d7d0d019ccf9f2e4dbf8d390527c1 MD5 | raw file
  1. <?php
  2. require_once RDFAPI_INCLUDE_DIR . 'model/Model.php';
  3. // ----------------------------------------------------------------------------------
  4. // Class: MemModel
  5. // ----------------------------------------------------------------------------------
  6. /**
  7. * A MemModel is an RDF Model, which is stored in the main memory.
  8. * This class provides methods for manipulating MemModels.
  9. *
  10. *
  11. *
  12. * @version $Id: MemModel.php,v 1.36 2007/05/01 12:59:18 cweiske Exp $
  13. * @author Chris Bizer <chris@bizer.de>
  14. * @author Gunnar AAstrand Grimnes <ggrimnes@csd.abdn.ac.uk>
  15. * @author Radoslaw Oldakowski <radol@gmx.de>
  16. * @author Daniel Westphal <mail@d-westphal.de>
  17. * @author Tobias Gauß <tobias.gauss@web.de>
  18. *
  19. * @package model
  20. * @access public
  21. */
  22. class MemModel extends Model {
  23. /**
  24. * Triples of the MemModel
  25. * @var array
  26. * @access private
  27. */
  28. var $triples = array();
  29. /**
  30. * Array containing the search indices
  31. * @var array['INDEX_TYPE'][]['label'][]['PosInModel']
  32. *
  33. * @access private
  34. */
  35. var $indexArr ;
  36. /**
  37. * depending on which index is used this variable is -1,0,1,2 or 3
  38. *
  39. * -1 : no index
  40. * 0 : default indices over subject, predicate, object separate
  41. * 1 : index over subject+predicate+object
  42. * 2 : index over subject+predicate
  43. * 3 : index over subject+object
  44. *
  45. * @var int
  46. * @access private
  47. */
  48. var $indexed;
  49. /**
  50. * Array of namespaces
  51. *
  52. * @var array
  53. * @access private
  54. */
  55. var $parsedNamespaces=array();
  56. /**
  57. * Constructor
  58. * You can supply a base_uri
  59. *
  60. * @param string $baseURI
  61. * @access public
  62. */
  63. function MemModel($baseURI = NULL) {
  64. $this->setBaseURI($baseURI);
  65. $this->indexed = INDEX_TYPE;
  66. }
  67. /**
  68. * Set a base URI for the MemModel.
  69. * Affects creating of new resources and serialization syntax.
  70. * If the URI doesn't end with # : or /, then a # is added to the URI.
  71. * @param string $uri
  72. * @access public
  73. */
  74. function setBaseURI($uri) {
  75. if ($uri != NULL) {
  76. $c = substr($uri, strlen($uri)-1 ,1);
  77. if (!($c=='#' || $c==':' || $c=='/' || $c=="\\"))
  78. $uri .= '#';
  79. }
  80. $this->baseURI = $uri;
  81. }
  82. /**
  83. * Number of triples in the MemModel
  84. *
  85. * @return integer
  86. * @access public
  87. */
  88. function size() {
  89. return count($this->triples);
  90. }
  91. /**
  92. * Checks if MemModel is empty
  93. *
  94. * @return boolean
  95. * @access public
  96. */
  97. function isEmpty() {
  98. if (count($this->triples) == 0) {
  99. return TRUE;
  100. } else {
  101. return FALSE;
  102. };
  103. }
  104. /**
  105. * Adds a new triple to the MemModel without checking if the statement is already in the MemModel.
  106. * So if you want a duplicate free MemModel use the addWithoutDuplicates() function (which is slower then add())
  107. *
  108. * @param object Statement $statement
  109. * @access public
  110. * @throws PhpError
  111. */
  112. function add($statement) {
  113. if (!is_a($statement, 'Statement')) {
  114. $errmsg = RDFAPI_ERROR . '(class: MemModel; method: add): Statement expected.';
  115. trigger_error($errmsg, E_USER_ERROR);
  116. }
  117. if($this->indexed != -1){
  118. $this->triples[] = $statement;
  119. end($this->triples);
  120. $k=key($this->triples);
  121. if($this->indexed==0){
  122. // index over S
  123. $this->_indexOpr($statement,$k,4,1);
  124. // index over P
  125. $this->_indexOpr($statement,$k,5,1);
  126. // index over O
  127. $this->_indexOpr($statement,$k,6,1);
  128. }else{
  129. $this->_indexOpr($statement,$k,$this->indexed,1);
  130. }
  131. }else{
  132. $this->triples[] = $statement;
  133. }
  134. }
  135. /**
  136. * Checks if a new statement is already in the MemModel and adds the statement, if it is not in the MemModel.
  137. * addWithoutDuplicates() is significantly slower then add().
  138. * Retruns TRUE if the statement is added.
  139. * FALSE otherwise.
  140. *
  141. * @param object Statement $statement
  142. * @return boolean
  143. * @access public
  144. * @throws PhpError
  145. */
  146. function addWithoutDuplicates($statement) {
  147. if (!is_a($statement, 'Statement')) {
  148. $errmsg = RDFAPI_ERROR . '(class: MemModel; method: addWithoutDuplicates): Statement expected.';
  149. trigger_error($errmsg, E_USER_ERROR);
  150. }
  151. if (!$this->contains($statement)) {
  152. $this->add($statement);
  153. return true;
  154. }else{
  155. return false;
  156. }
  157. }
  158. /**
  159. * Removes the triple from the MemModel.
  160. * TRUE if the triple is removed.
  161. * FALSE otherwise.
  162. *
  163. * @param object Statement $statement
  164. * @return boolean
  165. * @access public
  166. * @throws PhpError
  167. */
  168. function remove($statement) {
  169. if (!is_a($statement, 'Statement')) {
  170. $errmsg = RDFAPI_ERROR . '(class: MemModel; method: remove): Statement expected.';
  171. trigger_error($errmsg, E_USER_ERROR);
  172. }
  173. if($this->indexed==-1){
  174. $pass=false;
  175. foreach($this->triples as $key => $value) {
  176. if ($this->matchStatement($value, $statement->subject(), $statement->predicate(), $statement->object())) {
  177. unset($this->triples[$key]);
  178. $pass= true;
  179. }
  180. }
  181. return $pass;
  182. }else{
  183. $k= null;
  184. if($this->indexed==0){
  185. $pass=false;
  186. $del=false;
  187. while($del!=-1){
  188. // index over S
  189. $del=$this->_indexOpr($statement,$k,4,0);
  190. // index over P
  191. $this->_indexOpr($statement,$k,5,0);
  192. // index over O
  193. $this->_indexOpr($statement,$k,6,0);
  194. if($del!=-1){
  195. unset($this->triples[$del]);
  196. $pass=true;
  197. }
  198. }
  199. return $pass;
  200. }else{
  201. $pass=false;
  202. $del=false;
  203. while($del!=-1){
  204. $del=$this->_indexOpr($statement,$k,$this->indexed,0);
  205. if($del!=-1){
  206. unset($this->triples[$del]);
  207. $pass=true;
  208. }
  209. }
  210. return $pass;
  211. }
  212. }
  213. }
  214. /**
  215. * Short Dump of the MemModel.
  216. *
  217. * @access public
  218. * @return string
  219. */
  220. function toString() {
  221. return 'MemModel[baseURI=' . $this->getBaseURI() . '; size=' . $this->size() . ']';
  222. }
  223. /**
  224. * Dumps of the MemModel including all triples.
  225. *
  226. * @access public
  227. * @return string
  228. */
  229. function toStringIncludingTriples() {
  230. $dump = $this->toString() . chr(13);
  231. foreach($this->triples as $value) {
  232. $dump .= $value->toString() . chr(13);
  233. }
  234. return $dump;
  235. }
  236. /**
  237. * Writes the RDF serialization of the MemModel as HTML.
  238. *
  239. * @access public
  240. */
  241. function writeAsHtml() {
  242. require_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_RDF);
  243. $ser = new RdfSerializer();
  244. $rdf =& $ser->serialize($this);
  245. $rdf = htmlspecialchars($rdf, ENT_QUOTES);
  246. $rdf = str_replace(' ', '&nbsp;', $rdf);
  247. $rdf = nl2br($rdf);
  248. echo $rdf;
  249. }
  250. function writeAsRdf() {
  251. require_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_RDF);
  252. $ser = new RdfSerializer();
  253. $rdf =& $ser->serialize($this);
  254. $rdf = htmlspecialchars($rdf, ENT_QUOTES);
  255. $rdf = str_replace(' ', '&nbsp;', $rdf);
  256. $rdf = nl2br($rdf);
  257. echo $rdf;
  258. }
  259. /**
  260. * Writes the RDF serialization of the MemModel as HTML table.
  261. *
  262. * @access public
  263. */
  264. function writeAsHtmlTable() {
  265. // Import Package Utility
  266. include_once(RDFAPI_INCLUDE_DIR.PACKAGE_UTILITY);
  267. RDFUtil::writeHTMLTable($this);
  268. }
  269. /**
  270. * Writes the RDF serialization of the MemModel as HTML table.
  271. *
  272. * @access public
  273. * @return string
  274. */
  275. function writeRdfToString() {
  276. // Import Package Syntax
  277. include_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_RDF);
  278. $ser = new RdfSerializer();
  279. $rdf =& $ser->serialize($this);
  280. return $rdf;
  281. }
  282. /**
  283. * Saves the RDF,N3 or N-Triple serialization of the MemModel to a file.
  284. * You can decide to which format the model should be serialized by using a
  285. * corresponding suffix-string as $type parameter. If no $type parameter
  286. * is placed this method will serialize the model to XML/RDF format.
  287. * Returns FALSE if the MemModel couldn't be saved to the file.
  288. *
  289. * @access public
  290. * @param string $filename
  291. * @param string $type
  292. * @throws PhpError
  293. * @return boolean
  294. */
  295. function saveAs($filename, $type ='rdf') {
  296. // get suffix and create a corresponding serializer
  297. if ($type=='rdf') {
  298. // Import Package Syntax
  299. include_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_RDF);
  300. $ser=new RdfSerializer();
  301. }elseif ($type=='nt') {
  302. // Import Package Syntax
  303. include_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_N3);
  304. $ser=new NTripleSerializer();
  305. }elseif ($type=='n3') {
  306. // Import Package Syntax
  307. include_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_N3);
  308. $ser=new N3Serializer();
  309. }else {
  310. print ('Serializer type not properly defined. Use the strings "rdf","n3" or "nt".');
  311. return false;
  312. };
  313. return $ser->saveAs($this, $filename);
  314. }
  315. /**
  316. * Tests if the MemModel contains the given triple.
  317. * TRUE if the triple belongs to the MemModel;
  318. * FALSE otherwise.
  319. *
  320. * @param object Statement &$statement
  321. * @return boolean
  322. * @access public
  323. */
  324. function contains(&$statement) {
  325. // no index ->linear contains
  326. if ($this->indexed==-1){
  327. foreach($this->triples as $value) {
  328. if ($value->equals($statement)){
  329. return TRUE; }
  330. }
  331. return false;
  332. }
  333. if ($this->indexed==0){
  334. $res = $this->_containsIndex($statement,4);
  335. return $res;
  336. }else{
  337. return $this->_containsIndex($statement,$this->indexed);
  338. }
  339. }
  340. /**
  341. * Determine if all of the statements in a model are also contained in this MemModel.
  342. * True if all of the statements in $model are also contained in this MemModel and false otherwise.
  343. *
  344. * @param object Model &$model
  345. * @return boolean
  346. * @access public
  347. */
  348. function containsAll(&$model) {
  349. if (is_a($model, 'MemModel')) {
  350. foreach($model->triples as $statement)
  351. if(!$this->contains($statement))
  352. return FALSE;
  353. return TRUE;
  354. }elseif (is_a($model, 'DbModel'))
  355. return $model->containsAll($this);
  356. $errmsg = RDFAPI_ERROR . '(class: MemModel; method: containsAll): Model expected.';
  357. trigger_error($errmsg, E_USER_ERROR);
  358. }
  359. /**
  360. * Determine if any of the statements in a model are also contained in this MemModel.
  361. * True if any of the statements in $model are also contained in this MemModel and false otherwise.
  362. *
  363. * @param object Model &$model
  364. * @return boolean
  365. * @access public
  366. */
  367. function containsAny(&$model) {
  368. if (is_a($model, 'MemModel')) {
  369. foreach($model->triples as $modelStatement)
  370. if($this->contains($modelStatement))
  371. return TRUE;
  372. return FALSE;
  373. }elseif (is_a($model, 'DbModel'))
  374. return $model->containsAny($this);
  375. $errmsg = RDFAPI_ERROR . '(class: MemModel; method: containsAll): Model expected.';
  376. trigger_error($errmsg, E_USER_ERROR);
  377. }
  378. /**
  379. * Builds a search index for the statements in the MemModel.
  380. * The index is used by the find(),contains(),add() and remove() functions.
  381. * Performance example using a model with 43000 statements on a Linux machine:
  382. * Find without index takes 1.7 seconds.
  383. * Indexing takes 1.8 seconds.
  384. * Find with index takes 0.001 seconds.
  385. * So if you want to query a model more then once, build a index first.
  386. * The defaultindex is indices over subject, predicate, object seperate.
  387. *
  388. * mode = 0 : indices over subject,predicate,object (default)
  389. * mode = 1 : index over subject+predicate+object
  390. * mode = 2 : index over subject+predicate
  391. * mode = 3 : index over subject+object
  392. *
  393. * @param int $mode
  394. * @access public
  395. */
  396. function index($mode) {
  397. unset($this->indexArr);
  398. $this->indexArr=array();
  399. switch($mode){
  400. // unset indices
  401. case -1:
  402. $this->indexed=-1;
  403. unset($this->indexArr);
  404. break;
  405. // index over SPO
  406. case 0:
  407. $this->indexed=0;
  408. foreach($this->triples as $k => $t) {
  409. // index over S
  410. $this->_indexOpr($t,$k,4,1);
  411. // index over P
  412. $this->_indexOpr($t,$k,5,1);
  413. // index over O
  414. $this->_indexOpr($t,$k,6,1);
  415. }
  416. break;
  417. default:
  418. $this->indexed=$mode;
  419. foreach($this->triples as $k => $t) {
  420. $this->_indexOpr($t,$k,$this->indexed,1);
  421. }
  422. break;
  423. }
  424. }
  425. /**
  426. * Returns true if there is an index, false if not.
  427. *
  428. * @return boolean
  429. * @access public
  430. */
  431. function isIndexed() {
  432. if($this->indexed!=-1){
  433. return TRUE;
  434. }else{
  435. return FALSE;
  436. }
  437. }
  438. /**
  439. * Returns the indextype:
  440. * -1 if there is no index, 0 if there are indices over S,P,O(separate),
  441. * 1 if there is an index over SPO, 2 if there is an index over SP and 3 if
  442. * there is an index over SO.
  443. *
  444. * @return int
  445. * @access public
  446. *
  447. */
  448. function getIndexType(){
  449. return $this->indexed;
  450. }
  451. /**
  452. * General method to search for triples.
  453. * NULL input for any parameter will match anything.
  454. * Example: $result = $m->find( NULL, NULL, $node );
  455. * Finds all triples with $node as object.
  456. * Returns an empty MemModel if nothing is found.
  457. *
  458. * @param object Node $subject
  459. * @param object Node $predicate
  460. * @param object Node $object
  461. * @return object MemModel
  462. * @access public
  463. * @throws PhpError
  464. */
  465. function find($subject,$predicate,$object) {
  466. if (
  467. (!is_a($subject, 'Resource') && $subject != NULL) ||
  468. (!is_a($predicate, 'Resource') && $predicate != NULL) ||
  469. (!is_a($object, 'Node') && $object != NULL)
  470. ) {
  471. $errmsg = RDFAPI_ERROR . '(class: MemModel; method: find): Parameters must be subclasses of Node or NULL';
  472. trigger_error($errmsg, E_USER_ERROR);
  473. }
  474. $res = new MemModel($this->getBaseURI());
  475. $res->indexed=-1;
  476. if($this->isEmpty())
  477. return $res;
  478. if($subject == NULL && $predicate == NULL && $object == NULL)
  479. return $this;
  480. switch($this->indexed){
  481. case 1:
  482. if($subject!=NULL && $predicate != NULL && $object != NULL){
  483. $pos=$subject->getLabel().$predicate->getLabel().$object->getLabel();
  484. return $this->_findInIndex($pos,$subject,$predicate,$object,1);
  485. }else{
  486. break;
  487. }
  488. case 2:
  489. if($subject!=NULL && $predicate != NULL){
  490. $pos=$subject->getLabel().$predicate->getLabel();
  491. return $this->_findInIndex($pos,$subject,$predicate,$object,2);
  492. }else{
  493. break;
  494. }
  495. case 3:
  496. if($subject!=NULL && $object != NULL){
  497. $pos=$subject->getLabel().$object->getLabel();
  498. return $this->_findInIndex($pos,$subject,$predicate,$object,3);
  499. }else{
  500. break;
  501. }
  502. case 0:
  503. if($subject!= null){
  504. $pos=$subject->getLabel();
  505. return $this->_findInIndex($pos,$subject,$predicate,$object,4);
  506. }
  507. if($predicate!= null){
  508. $pos=$predicate->getLabel();
  509. return $this->_findInIndex($pos,$subject,$predicate,$object,5);
  510. }
  511. if($object!= null){
  512. $pos=$object->getLabel();
  513. return $this->_findInIndex($pos,$subject,$predicate,$object,6);
  514. }
  515. }
  516. // if no index: linear search
  517. foreach($this->triples as $value) {
  518. if ($this->matchStatement($value, $subject, $predicate, $object))
  519. $res->add($value);
  520. }
  521. return $res;
  522. }
  523. /**
  524. * Method to search for triples using Perl-style regular expressions.
  525. * NULL input for any parameter will match anything.
  526. * Example: $result = $m->find_regex( NULL, NULL, $regex );
  527. * Finds all triples where the label of the object node matches the regular expression.
  528. * Returns an empty MemModel if nothing is found.
  529. *
  530. * @param string $subject_regex
  531. * @param string $predicate_regex
  532. * @param string $object_regex
  533. * @return object MemModel
  534. * @access public
  535. */
  536. function findRegex($subject_regex, $predicate_regex, $object_regex) {
  537. $res = new MemModel($this->getBaseURI());
  538. if($this->size() == 0)
  539. return $res;
  540. if($subject_regex == NULL && $predicate_regex == NULL && $object_regex == NULL)
  541. return $this;
  542. foreach($this->triples as $value) {
  543. if (
  544. ($subject_regex == NULL || preg_match($subject_regex, $value->subj->getLabel())) &&
  545. ($predicate_regex == NULL || preg_match($predicate_regex, $value->pred->getLabel())) &&
  546. ($object_regex == NULL || preg_match($object_regex, $value->obj->getLabel()))
  547. ) $res->add($value);
  548. }
  549. return $res;
  550. }
  551. /**
  552. * Returns all tripels of a certain vocabulary.
  553. * $vocabulary is the namespace of the vocabulary inluding a # : / char at the end.
  554. * e.g. http://www.w3.org/2000/01/rdf-schema#
  555. * Returns an empty MemModel if nothing is found.
  556. *
  557. * @param string $vocabulary
  558. * @return object MemModel
  559. * @access public
  560. */
  561. function findVocabulary($vocabulary) {
  562. if($this->size() == 0)
  563. return new MemModel();
  564. if($vocabulary == NULL || $vocabulary == '')
  565. return $this;
  566. $res = new MemModel($this->getBaseURI());
  567. if($this->indexed==0){
  568. foreach($this->indexArr[5] as $key => $value){
  569. $pos=strpos($key,'#')+1;
  570. if(substr($key,0,$pos)==$vocabulary){
  571. for($i=1;$i<=$value[0];$i++){
  572. $res->add($this->triples[$value[$i]]);
  573. }
  574. }
  575. }
  576. return $res;
  577. }else{
  578. // Import Package Utility
  579. include_once(RDFAPI_INCLUDE_DIR.PACKAGE_UTILITY);
  580. foreach($this->triples as $value) {
  581. if (RDFUtil::getNamespace($value->getPredicate()) == $vocabulary)
  582. $res->add($value);
  583. }
  584. return $res;
  585. }
  586. }
  587. /**
  588. * Searches for triples and returns the first matching statement.
  589. * NULL input for any parameter will match anything.
  590. * Example: $result = $m->findFirstMatchingStatement( NULL, NULL, $node );
  591. * Returns the first statement of the MemModel where the object equals $node.
  592. * Returns an NULL if nothing is found.
  593. * You can define an offset to search for. Default = 0
  594. *
  595. * @param object Node $subject
  596. * @param object Node $predicate
  597. * @param object Node $object
  598. * @param integer $offset
  599. * @return object Statement
  600. * @access public
  601. */
  602. function findFirstMatchingStatement($subject, $predicate, $object, $offset = 0) {
  603. $currentOffset = 0;
  604. for($i=0;$i<=$offset;$i++)
  605. {
  606. $res = $this->findFirstMatchOff($subject, $predicate, $object, $currentOffset);
  607. $currentOffset=$res+1;
  608. }
  609. if ($res != -1) {
  610. return $this->triples[$res];
  611. } else {
  612. return NULL;
  613. }
  614. }
  615. /**
  616. * Searches for triples and returns the first matching statement from a given offset.
  617. * This method is used by the util/findIterator. NULL input for any parameter will match anything.
  618. * Example: $result = $m->findFirstMatchingStatement( NULL, NULL, $node, $off );
  619. * Returns the position of the first statement of the MemModel where the object equals $node from the given
  620. * offset.
  621. * Returns an -1 if nothing is found.
  622. *
  623. * @param object Node $subject
  624. * @param object Node $predicate
  625. * @param object Node $object
  626. * @param int $off
  627. * @return int
  628. * @access private
  629. */
  630. function findFirstMatchOff($subject,$predicate, $object,$off) {
  631. if (
  632. (!is_a($subject, 'Resource') && $subject != NULL) ||
  633. (!is_a($predicate, 'Resource') && $predicate != NULL) ||
  634. (!is_a($object, 'Node') && $object != NULL)
  635. ) {
  636. $errmsg = RDFAPI_ERROR . '(class: MemModel; method: find): Parameters must be subclasses of Node or NULL';
  637. trigger_error($errmsg, E_USER_ERROR);
  638. }
  639. $match=-1;
  640. $ind=$this->indexed;
  641. if($subject == NULL && $predicate == NULL && $object == NULL)
  642. {
  643. foreach ($this->triples as $key => $statement)
  644. {
  645. if ($key >= $off)
  646. return $key;
  647. }
  648. return -1;
  649. }
  650. switch($ind){
  651. case 1:
  652. if($subject!=NULL && $predicate != NULL && $object != NULL){
  653. $pos=$subject->getLabel().$predicate->getLabel().$object->getLabel();
  654. return $this->_findMatchIndex($pos,$subject,$predicate,$object,1,$off);
  655. }else{
  656. break;
  657. }
  658. case 2:
  659. if($subject!=NULL && $predicate != NULL){
  660. $pos=$subject->getLabel().$predicate->getLabel();
  661. return $this->_findMatchIndex($pos,$subject,$predicate,$object,2,$off);
  662. }else{
  663. break;
  664. }
  665. case 3:
  666. if($subject!=NULL && $object != NULL){
  667. $pos=$subject->getLabel().$object->getLabel();
  668. return $this->_findMatchIndex($pos,$subject,$predicate,$object,3,$off);
  669. }else{
  670. break;
  671. }
  672. case 0:
  673. if($subject!= null){
  674. $pos=$subject->getLabel();
  675. return $this->_findMatchIndex($pos,$subject,$predicate,$object,4,$off);
  676. }
  677. if($predicate!= null){
  678. $pos=$predicate->getLabel();
  679. return $this->_findMatchIndex($pos,$subject,$predicate,$object,5,$off);
  680. }
  681. if($object!= null){
  682. $pos=$object->getLabel();
  683. return $this->_findMatchIndex($pos,$subject,$predicate,$object,6,$off);
  684. }
  685. break;
  686. }
  687. // if no index: linear search
  688. foreach($this->triples as $key => $value){
  689. if ($this->matchStatement($value, $subject, $predicate, $object)){
  690. if($off<=$key){
  691. $match=$key;
  692. break;
  693. }
  694. }
  695. }
  696. return $match;
  697. }
  698. /**
  699. * Searches for triples and returns the number of matches.
  700. * NULL input for any parameter will match anything.
  701. * Example: $result = $m->findCount( NULL, NULL, $node );
  702. * Finds all triples with $node as object.
  703. *
  704. * @param object Node $subject
  705. * @param object Node $predicate
  706. * @param object Node $object
  707. * @return integer
  708. * @access public
  709. */
  710. function findCount($subject, $predicate, $object) {
  711. $res = $this->find($subject, $predicate, $object);
  712. return $res->size();
  713. }
  714. /**
  715. * Perform an RDQL query on this MemModel.
  716. * This method returns an associative array of variable bindings.
  717. * The values of the query variables can either be RAP's objects (instances of Node)
  718. * if $returnNodes set to TRUE, or their string serialization.
  719. *
  720. * @access public
  721. * @param string $queryString
  722. * @param boolean $returnNodes
  723. * @return array [][?VARNAME] = object Node (if $returnNodes = TRUE)
  724. * OR array [][?VARNAME] = string
  725. *
  726. */
  727. function rdqlQuery($queryString, $returnNodes = TRUE) {
  728. // Import RDQL Package
  729. include_once(RDFAPI_INCLUDE_DIR.PACKAGE_RDQL);
  730. $parser = new RdqlParser();
  731. $parsedQuery =& $parser->parseQuery($queryString);
  732. // this method can only query this MemModel
  733. // if another model was specified in the from clause throw an error
  734. if (isset($parsedQuery['sources'][1])) {
  735. $errmsg = RDFAPI_ERROR . '(class: MemModel; method: rdqlQuery):';
  736. $errmsg .= ' this method can only query this MemModel';
  737. trigger_error($errmsg, E_USER_ERROR);
  738. }
  739. $engine = new RdqlMemEngine();
  740. $res =& $engine->queryModel($this, $parsedQuery, $returnNodes);
  741. return $res;
  742. }
  743. /**
  744. * Perform an RDQL query on this MemModel.
  745. * This method returns an RdqlResultIterator of variable bindings.
  746. * The values of the query variables can either be RAP's objects (instances of Node)
  747. * if $returnNodes set to TRUE, or their string serialization.
  748. *
  749. * @access public
  750. * @param string $queryString
  751. * @param boolean $returnNodes
  752. * @return object RdqlResultIterator = with values as object Node (if $returnNodes = TRUE)
  753. * OR object RdqlResultIterator = with values as strings if (if $returnNodes = FALSE)
  754. *
  755. */
  756. function rdqlQueryAsIterator($queryString, $returnNodes = TRUE) {
  757. // Import RDQL Package
  758. include_once(RDFAPI_INCLUDE_DIR.PACKAGE_RDQL);
  759. return new RdqlResultIterator($this->rdqlQuery($queryString, $returnNodes));
  760. }
  761. /**
  762. * General method to replace nodes of a MemModel.
  763. * NULL input for any parameter will match nothing.
  764. * Example: $m->replace($node, NULL, $node, $replacement);
  765. * Replaces all $node objects beeing subject or object in
  766. * any triple of the MemModel with the $needle node.
  767. *
  768. * @param object Node $subject
  769. * @param object Node $predicate
  770. * @param object Node $object
  771. * @param object Node $replacement
  772. * @access public
  773. * @throws PhpError
  774. */
  775. function replace($subject, $predicate, $object, $replacement) {
  776. if (
  777. (!is_a($replacement, 'Node')) ||
  778. (!is_a($subject, 'Resource') && $subject != NULL) ||
  779. (!is_a($predicate, 'Resource') && $predicate != NULL) ||
  780. (!is_a($object, 'Node') && $object != NULL)
  781. ) {
  782. $errmsg = RDFAPI_ERROR . '(class: MemModel; method: replace): Parameters must be subclasses of Node or NULL';
  783. trigger_error($errmsg, E_USER_ERROR);
  784. }
  785. if($this->size() == 0)
  786. break;
  787. foreach($this->triples as $key => $value) {
  788. if ($this->triples[$key]->subj->equals($subject)) {
  789. $this->triples[$key]->subj = $replacement;
  790. }
  791. if ($this->triples[$key]->pred->equals($predicate))
  792. $this->triples[$key]->pred = $replacement;
  793. if ($this->triples[$key]->obj->equals($object))
  794. $this->triples[$key]->obj = $replacement;
  795. }
  796. $this->index($this->indexed);
  797. }
  798. /**
  799. * Internal method that checks, if a statement matches a S, P, O or NULL combination.
  800. * NULL input for any parameter will match anything.
  801. *
  802. * @param object Statement $statement
  803. * @param object Node $subject
  804. * @param object Node $predicate
  805. * @param object Node $object
  806. * @return boolean
  807. * @access private
  808. */
  809. function matchStatement($statement, $subject, $predicate, $object) {
  810. if(($subject != NULL) AND !($statement->subj->equals($subject)))
  811. return false;
  812. if($predicate != NULL && !($statement->pred->equals($predicate)))
  813. return false;
  814. if($object != NULL && !($statement->obj->equals($object)))
  815. return false;
  816. return true;
  817. }
  818. /**
  819. * Checks if two models are equal.
  820. * Two models are equal if and only if the two RDF graphs they represent are isomorphic.
  821. *
  822. * @access public
  823. * @param object model &$that
  824. * @throws phpErrpr
  825. * @return boolean
  826. */
  827. function equals(&$that) {
  828. if (!is_a($that, 'Model')) {
  829. $errmsg = RDFAPI_ERROR . '(class: MemModel; method: equals): Model expected.';
  830. trigger_error($errmsg, E_USER_ERROR);
  831. }
  832. if ($this->size() != $that->size())
  833. return FALSE;
  834. /*
  835. if (!$this->containsAll($that))
  836. return FALSE;
  837. return TRUE;
  838. */
  839. include_once(RDFAPI_INCLUDE_DIR. "util/ModelComparator.php");
  840. return ModelComparator::compare($this,$that);
  841. }
  842. /**
  843. * Returns a new MemModel that is the set-union of the MemModel with another model.
  844. * Duplicate statements are removed. If you want to allow duplicates, use addModel() which is much faster.
  845. *
  846. * The result of taking the set-union of two or more RDF graphs (i.e. sets of triples)
  847. * is another graph, which we will call the merge of the graphs.
  848. * Each of the original graphs is a subgraph of the merged graph. Notice that when forming
  849. * a merged graph, two occurrences of a given uriref or literal as nodes in two different
  850. * graphs become a single node in the union graph (since by definition they are the same
  851. * uriref or literal) but blank nodes are not 'merged' in this way; and arcs are of course
  852. * never merged. In particular, this means that every blank node in a merged graph can be
  853. * identified as coming from one particular graph in the original set of graphs.
  854. *
  855. * Notice that one does not, in general, obtain the merge of a set of graphs by concatenating
  856. * their corresponding N-triples documents and constructing the graph described by the merged
  857. * document, since if some of the documents use the same node identifiers, the merged document
  858. * will describe a graph in which some of the blank nodes have been 'accidentally' merged.
  859. * To merge Ntriples documents it is necessary to check if the same nodeID is used in two or
  860. * more documents, and to replace it with a distinct nodeID in each of them, before merging the
  861. * documents. (Not implemented yet !!!!!!!!!!!)
  862. *
  863. * @param object Model $model
  864. * @return object MemModel
  865. * @access public
  866. * @throws phpErrpr
  867. *
  868. */
  869. function & unite(&$model) {
  870. if (!is_a($model, 'Model')) {
  871. $errmsg = RDFAPI_ERROR . '(class: MemModel; method: unite): Model expected.';
  872. trigger_error($errmsg, E_USER_ERROR);
  873. }
  874. $res = $this;
  875. if (is_a($model, 'MemModel')) {
  876. require_once RDFAPI_INCLUDE_DIR . 'util/StatementIterator.php';
  877. $stateIt=new StatementIterator($model);
  878. while($statement=$stateIt->next())
  879. {
  880. $res->addWithoutDuplicates($statement);
  881. }
  882. }
  883. elseif (is_a($model, 'DbModel')) {
  884. $memModel =& $model->getMemModel();
  885. foreach($memModel->triples as $value)
  886. $res->addWithoutDuplicates($value);
  887. }
  888. return $res;
  889. }
  890. /**
  891. * Returns a new MemModel that is the subtraction of another model from this MemModel.
  892. *
  893. * @param object Model $model
  894. * @return object MemModel
  895. * @access public
  896. * @throws phpErrpr
  897. */
  898. function & subtract(&$model) {
  899. if (!is_a($model, 'Model')) {
  900. $errmsg = RDFAPI_ERROR . '(class: MemModel; method: subtract): Model expected.';
  901. trigger_error($errmsg, E_USER_ERROR);
  902. }
  903. $res = $this;
  904. if (is_a($model, 'MemModel'))
  905. {
  906. require_once RDFAPI_INCLUDE_DIR . 'util/StatementIterator.php';
  907. $stateIt=new StatementIterator($model);
  908. while($statement=$stateIt->next())
  909. {
  910. $res->remove($statement);
  911. }
  912. }
  913. elseif (is_a($model, 'DbModel'))
  914. {
  915. $memModel =& $model->getMemModel();
  916. foreach($memModel->triples as $value)
  917. $res->remove($value);
  918. }
  919. return $res;
  920. }
  921. /**
  922. * Returns a new MemModel containing all the statements which are in both this MemModel and another.
  923. *
  924. * @param object Model $model
  925. * @return object MemModel
  926. * @access public
  927. * @throws phpErrpr
  928. */
  929. function & intersect(&$model) {
  930. if (!is_a($model, 'Model')) {
  931. $errmsg = RDFAPI_ERROR . '(class: MemModel; method: intersect: Model expected.';
  932. trigger_error($errmsg, E_USER_ERROR);
  933. }
  934. $res = new MemModel($this->getBaseURI());
  935. if (is_a($model, 'DbModel') || is_a($model, 'RDFSBModel'))
  936. {
  937. $memModel =& $model->getMemModel();
  938. foreach($memModel->triples as $value) {
  939. if ($this->contains($value))
  940. $res->add($value);
  941. }
  942. }
  943. elseif (is_a($model, 'MemModel'))
  944. {
  945. foreach($model->triples as $value) {
  946. if ($this->contains($value))
  947. $res->add($value);
  948. }
  949. }
  950. return $res;
  951. }
  952. /**
  953. * Adds another model to this MemModel.
  954. * Duplicate statements are not removed.
  955. * If you don't want duplicates, use unite().
  956. * If any statement of the model to be added to this model contains a blankNode
  957. * with an identifier already existing in this model, a new blankNode is generated.
  958. *
  959. * @param object Model $model
  960. * @access public
  961. * @throws phpErrpr
  962. *
  963. */
  964. function addModel(&$model) {
  965. if (!is_a($model, 'Model')) {
  966. $errmsg = RDFAPI_ERROR . '(class: MemModel; method: addModel): Model expected.';
  967. trigger_error($errmsg, E_USER_ERROR);
  968. }
  969. $blankNodes_tmp = array();
  970. if (is_a($model, 'MemModel')) {
  971. require_once RDFAPI_INCLUDE_DIR . 'util/StatementIterator.php';
  972. $stateIt=new StatementIterator($model);
  973. while($statement=$stateIt->next())
  974. {
  975. $this->_addStatementFromAnotherModel($statement, $blankNodes_tmp);
  976. };
  977. $this->addParsedNamespaces($model->getParsedNamespaces());
  978. }
  979. elseif (is_a($model, 'DbModel')) {
  980. $memModel =& $model->getMemModel();
  981. foreach($memModel->triples as $value)
  982. $this->_addStatementFromAnotherModel($value, $blankNodes_tmp);
  983. }
  984. $this->index($this->indexed);
  985. }
  986. /**
  987. * Reifies the MemModel.
  988. * Returns a new MemModel that contains the reifications of all statements of this MemModel.
  989. *
  990. * @access public
  991. * @return object MemModel
  992. */
  993. function & reify() {
  994. $res = new MemModel($this->getBaseURI());
  995. $stateIt=$this->getStatementIterator();
  996. while($statement=$stateIt->next())
  997. {
  998. $pointer =& $statement->reify($res);
  999. $res->addModel($pointer);
  1000. };
  1001. return $res;
  1002. }
  1003. /**
  1004. * Returns a StatementIterator for traversing the MemModel.
  1005. * @access public
  1006. * @return object StatementIterator
  1007. */
  1008. function & getStatementIterator() {
  1009. // Import Package Utility
  1010. require_once RDFAPI_INCLUDE_DIR . 'util/StatementIterator.php';
  1011. $si = new StatementIterator($this);
  1012. return $si;
  1013. }
  1014. /**
  1015. * Returns a FindIterator for traversing the MemModel.
  1016. * @access public
  1017. * @return object FindIterator
  1018. */
  1019. function & findAsIterator($sub=null,$pred=null,$obj=null) {
  1020. // Import Package Utility
  1021. require_once RDFAPI_INCLUDE_DIR . 'util/FindIterator.php';
  1022. $if = new FindIterator($this,$sub,$pred,$obj);
  1023. return $if;
  1024. }
  1025. /**
  1026. * Returns a FindIterator for traversing the MemModel.
  1027. * @access public
  1028. * @return object FindIterator
  1029. */
  1030. function & iterFind($sub=null,$pred=null,$obj=null) {
  1031. // Import Package Utility
  1032. require_once RDFAPI_INCLUDE_DIR . 'util/IterFind.php';
  1033. $if = new IterFind($this,$sub,$pred,$obj);
  1034. return $if;
  1035. }
  1036. /**
  1037. * Returns the models namespaces.
  1038. *
  1039. * @author Tobias Gau?<tobias.gauss@web.de>
  1040. * @access public
  1041. * @return Array
  1042. */
  1043. function getParsedNamespaces(){
  1044. if(count($this->parsedNamespaces)!=0){
  1045. return $this->parsedNamespaces;
  1046. }else{
  1047. return false;
  1048. }
  1049. }
  1050. /**
  1051. * Adds the namespaces to the model. This method is called by
  1052. * the parser. !!!! addParsedNamespaces() not overwrites manual
  1053. * added namespaces in the model !!!!
  1054. *
  1055. * @author Tobias Gau?<tobias.gauss@web.de>
  1056. * @access public
  1057. * @param Array $newNs
  1058. */
  1059. function addParsedNamespaces($newNs){
  1060. if($newNs)
  1061. $this->parsedNamespaces = $this->parsedNamespaces + $newNs;
  1062. }
  1063. /**
  1064. * Adds a namespace and prefix to the model.
  1065. *
  1066. * @author Tobias Gau?<tobias.gauss@web.de>
  1067. * @access public
  1068. * @param String
  1069. * @param String
  1070. */
  1071. function addNamespace($prefix, $nmsp){
  1072. $this->parsedNamespaces[$nmsp]=$prefix;
  1073. }
  1074. /**
  1075. * removes a single namespace from the model
  1076. *
  1077. * @author Tobias Gau?<tobias.gauss@web.de>
  1078. * @access public
  1079. * @param String $nmsp
  1080. */
  1081. function removeNamespace($nmsp){
  1082. if(isset($this->parsedNamespaces[$nmsp])){
  1083. unset($this->parsedNamespaces[$nmsp]);
  1084. return true;
  1085. }else{
  1086. return false;
  1087. }
  1088. }
  1089. /**
  1090. * Close the MemModel and free up resources held.
  1091. *
  1092. * @access public
  1093. */
  1094. function close() {
  1095. unset( $this->baseURI );
  1096. unset( $this->triples );
  1097. }
  1098. // =============================================================================
  1099. // *************************** helper functions ********************************
  1100. // =============================================================================
  1101. /**
  1102. * Checks if $statement is in index
  1103. *
  1104. * @param int $ind
  1105. * @param Statement &$statement
  1106. * @return boolean
  1107. * @access private
  1108. */
  1109. function _containsIndex(&$statement,$ind){
  1110. switch($ind){
  1111. case 4:
  1112. $sub=$statement->getSubject();
  1113. $pos=$sub->getLabel();
  1114. break;
  1115. case 1:
  1116. $sub=$statement->getSubject();
  1117. $pred=$statement->getPredicate();
  1118. $obj=$statement->getObject();
  1119. $pos=$sub->getLabel().$pred->getLabel().$obj->getLabel();
  1120. break;
  1121. case 2:
  1122. $sub=$statement->getSubject();
  1123. $pred=$statement->getPredicate();
  1124. $pos=$sub->getLabel().$pred->getLabel();
  1125. break;
  1126. case 3:
  1127. $sub=$statement->getSubject();
  1128. $obj=$statement->getObject();
  1129. $pos=$sub->getLabel().$obj->getLabel();
  1130. break;
  1131. }
  1132. if (!isset($this->indexArr[$ind][$pos]))
  1133. return FALSE;
  1134. foreach ($this->indexArr[$ind][$pos] as $key => $value) {
  1135. $t=$this->triples[$value];
  1136. if ($t->equals($statement))
  1137. return TRUE;
  1138. }
  1139. return FALSE;
  1140. }
  1141. /**
  1142. * finds a statement in an index. $pos is the Position in the index
  1143. * and $ind the adequate searchindex
  1144. *
  1145. * @param String $pos
  1146. * @param Object Subject &$subject
  1147. * @param Object Predicate &$predicate
  1148. * @param Object Object &$object
  1149. * @param int &ind
  1150. * @return MemModel $res
  1151. * @access private
  1152. */
  1153. function _findInIndex($pos,&$subject,&$predicate,&$object,$ind){
  1154. $res = new MemModel($this->getBaseURI());
  1155. $res->indexed=-1;
  1156. if (!isset($this->indexArr[$ind][$pos]))
  1157. return $res;
  1158. foreach($this->indexArr[$ind][$pos] as $key =>$value){
  1159. $t=$this->triples[$value];
  1160. if ($this->matchStatement($t,$subject,$predicate,$object))
  1161. $res->add($t);
  1162. }
  1163. return $res;
  1164. }
  1165. /**
  1166. * adds/removes a statement into/from an index.
  1167. * mode=0 removes the statement from the index;
  1168. * mode=1 adds the statement into the index.
  1169. * returns the statements position.
  1170. *
  1171. * @param Object Statement &$statement
  1172. * @param int $k
  1173. * @param int $ind
  1174. * @param int $mode
  1175. * @return int $k
  1176. * @access private
  1177. */
  1178. function _indexOpr(&$statement,$k,$ind,$mode){
  1179. // determine position in adequate index
  1180. switch($ind){
  1181. case 1:
  1182. $s=$statement->getSubject();
  1183. $p=$statement->getPredicate();
  1184. $o=$statement->getObject();
  1185. $pos=$s->getLabel().$p->getLabel().$o->getLabel();
  1186. break;
  1187. case 2:
  1188. $s=$statement->getSubject();
  1189. $p=$statement->getPredicate();
  1190. $pos=$s->getLabel().$p->getLabel();
  1191. break;
  1192. case 3:
  1193. $s=$statement->getSubject();
  1194. $o=$statement->getObject();
  1195. $pos=$s->getLabel().$o->getLabel();
  1196. break;
  1197. case 4:
  1198. $s=$statement->getSubject();
  1199. $pos=$s->getLabel();
  1200. break;
  1201. case 5:
  1202. $p=$statement->getPredicate();
  1203. $pos=$p->getLabel();
  1204. break;
  1205. case 6:
  1206. $o=$statement->getObject();
  1207. $pos=$o->getLabel();
  1208. break;
  1209. }
  1210. switch($mode){
  1211. // add in Index
  1212. case 1:
  1213. if(isset($this->indexArr[$ind][$pos])){
  1214. $this->indexArr[$ind][$pos][] = $k;
  1215. }else{
  1216. $this->indexArr[$ind][$pos][0] = $k;
  1217. }
  1218. break;
  1219. // remove from Index
  1220. case 0:
  1221. $subject=$statement->getSubject();
  1222. $predicate=$statement->getPredicate();
  1223. $object=$statement->getObject();
  1224. $k=-1;
  1225. if(!isset($this->indexArr[$ind][$pos])){
  1226. return -1;
  1227. }
  1228. $num=count($this->indexArr[$ind][$pos]);
  1229. foreach($this->indexArr[$ind][$pos] as $key => $value){
  1230. $t=$this->triples[$value];
  1231. if($this->matchStatement($t,$subject,$predicate,$object)){
  1232. $k=$value;
  1233. if($num==1){
  1234. unset($this->indexArr[$ind][$pos]);
  1235. }else{
  1236. unset($this->indexArr[$ind][$pos][$key]);
  1237. }
  1238. return $k;
  1239. }
  1240. }
  1241. break;
  1242. }
  1243. return $k;
  1244. }
  1245. /**
  1246. * finds next or previous matching statement.
  1247. * Returns Position in model or -1 if there is no match.
  1248. *
  1249. *
  1250. * @param String
  1251. * @param object Subject
  1252. * @param object Predicate
  1253. * @param object Object
  1254. * @param integer
  1255. * @param integer
  1256. * @return integer
  1257. * @access private
  1258. */
  1259. function _findMatchIndex($pos,&$s,&$p,&$o,$ind,$off){
  1260. $match=-1;
  1261. if (!isset($this->indexArr[$ind][$pos])) {
  1262. return $match;}
  1263. foreach($this->indexArr[$ind][$pos] as $key =>$value){
  1264. $t=$this->triples[$value];
  1265. if ($this->matchStatement($t,$s,$p,$o)){
  1266. if($off <= $value){
  1267. $match= $value;
  1268. return $match;
  1269. }
  1270. }
  1271. }
  1272. return $match;
  1273. }
  1274. } // end: MemModel
  1275. ?>