PageRenderTime 52ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/tdt/core/model/semantics/rdfapi-php/api/model/MemModel.php

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