-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
296 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
<?php | ||
/** | ||
* @file | ||
*/ | ||
|
||
namespace JSKOS; | ||
|
||
/** | ||
* A JSKOS Set as defined by JSKOS specification. | ||
* | ||
* A Set is a possibly empty array with all members being JSKOS Objects | ||
* with distinct values in field `uri` (if given), expect the last member | ||
* optionally being null. | ||
*/ | ||
class Set extends PrettyJsonSerializable implements \Countable, \ArrayAccess, \IteratorAggregate | ||
{ | ||
private $members = []; | ||
private $incomplete = false; | ||
|
||
/** | ||
* Create a new JSKOS set. | ||
* @param Boolean $incomplete whether the set is incomplete (false by default) | ||
*/ | ||
public function __construct($incomplete = false) | ||
{ | ||
$this->incomplete = $incomplete; | ||
} | ||
|
||
/** | ||
* Return the number of known values in this set. | ||
* Note that an incomplete set has zero known values but is not empty! | ||
*/ | ||
public function count() | ||
{ | ||
return count($this->members); | ||
} | ||
|
||
/** | ||
* Return whether this set is empty. | ||
* Note that an incomplete set is never empty! | ||
*/ | ||
public function isEmpty() | ||
{ | ||
return !count($this->members) && !$this->incomplete; | ||
} | ||
|
||
/** | ||
* Return whether this set is incomplete. | ||
*/ | ||
public function isIncomplete() | ||
{ | ||
return $this->incomplete; | ||
} | ||
|
||
/** | ||
* Return whether this set is complete. | ||
*/ | ||
public function isComplete() | ||
{ | ||
return !$this->incomplete; | ||
} | ||
|
||
/** | ||
* Make this set complete (default) or incomplete. | ||
* @param Boolean $complete | ||
*/ | ||
public function setComplete($complete = true) | ||
{ | ||
$this->incomplete = !$complete; | ||
} | ||
|
||
/** | ||
* Return whether this set contains an Object with given URI. | ||
*/ | ||
public function hasURI($uri) | ||
{ | ||
return !is_null($this->findURI($uri)); | ||
} | ||
|
||
/** | ||
* Return the offset of a member Object with given URI, if it exists. | ||
*/ | ||
public function findURI($uri) | ||
{ | ||
if (is_string($uri)) { // TODO: URI syntax check? | ||
foreach ($this->members as $i => $object) { | ||
if ($object->uri == $uri) { | ||
return $i; | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
/** | ||
* Return an array of URIs of Objects in this set. | ||
*/ | ||
public function getURIs() | ||
{ | ||
$uris = []; | ||
foreach ($this->members as $object) { | ||
$uri = $object->uri; | ||
if ($uri) { | ||
$uris[] = $uri; | ||
} | ||
} | ||
return $uris; | ||
} | ||
|
||
/** | ||
* Return whether this set does not contain same objects. | ||
*/ | ||
public function isValid() | ||
{ | ||
$uris = []; | ||
foreach ($this->members as $object) { | ||
$uri = $object->uri; | ||
if ($uri) { | ||
if (isset($uris[$uri])) { | ||
return false; | ||
} else { | ||
$uris[$uri] = true; | ||
} | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
# ArrayAccess -> | ||
|
||
/** | ||
* Return whether an object exists at the given offset. | ||
* @param mixed $offset | ||
*/ | ||
public function offsetExists($offset) | ||
{ | ||
return isset($this->members[$offset]); | ||
} | ||
|
||
/** | ||
* Return an object at the given offset. | ||
* @param mixed $offset | ||
*/ | ||
public function offsetGet($offset) | ||
{ | ||
return $this->members[$offset]; | ||
} | ||
|
||
/** | ||
* Set an object at the given offset. | ||
* @param mixed $offset | ||
* @param mixed $object | ||
*/ | ||
public function offsetSet($offset, $object) | ||
{ | ||
if ($object instanceof \JSKOS\Object) { | ||
if (is_int($offset) && $offset >= 0 && $offset < $this->count()) { | ||
$this->members[$offset] = $object; | ||
} else { | ||
$this->members[] = $object; | ||
} | ||
} else { | ||
throw new \BadMethodCallException(__CLASS__ . ' can only contain JSKOS\Object instances'); | ||
} | ||
} | ||
|
||
/** | ||
* @throws \BadMethodCallException | ||
*/ | ||
public function offsetUnset($key) | ||
{ | ||
throw new \BadMethodCallException(__METHOD__ . ' is not supported'); | ||
} | ||
|
||
# <- ArrayAccess | ||
# IteratorAggregate -> | ||
|
||
/** | ||
* Return an iterator to iterate all members of the set. | ||
*/ | ||
public function getIterator() | ||
{ | ||
return new \ArrayIterator($this->members); | ||
} | ||
|
||
# <- IteratorAggregate | ||
# PrettyJsonSerializable -> | ||
|
||
/** | ||
* Return a data structure to serialize this set as JSON. | ||
*/ | ||
public function jsonSerialize() | ||
{ | ||
$serialize = function ($x) { return $x->jsonSerialize(); }; | ||
$set = array_map($serialize, $this->members); | ||
if ($this->incomplete) { | ||
$set[] = null; | ||
} | ||
return $set; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
<?php | ||
|
||
namespace JSKOS; | ||
|
||
/** | ||
* @covers \JSKOS\Set | ||
*/ | ||
class SetTest extends \PHPUnit_Framework_TestCase | ||
{ | ||
public function testEmptySet() | ||
{ | ||
$set = new Set(); | ||
$this->assertInstanceOf('JSKOS\Set', $set); | ||
$this->assertEquals(0, count($set), 'empty set has size zero'); | ||
$this->assertTrue($set->isEmpty(), 'empty set is empty'); | ||
$this->assertEquals(json_encode([]), json_encode($set), 'empty set is []'); | ||
} | ||
|
||
public function testIncompleteSet() | ||
{ | ||
$set = new Set(); | ||
$this->assertFalse($set->isIncomplete(), 'empty set is not incomplete'); | ||
$this->assertTrue($set->isComplete(), 'empty set is complete'); | ||
|
||
$set = new Set(true); | ||
$this->assertTrue($set->isIncomplete(), 'incomplete set is incomplete'); | ||
$this->assertFalse($set->isComplete(), 'incomplete set is not complete'); | ||
$this->assertFalse($set->isEmpty(), 'incomplete set is not empty'); | ||
|
||
$set->setComplete(); | ||
$this->assertTrue($set->isComplete(), 'marked set as complete set'); | ||
|
||
$set->setComplete(false); | ||
$this->assertTrue($set->isIncomplete(), 'marked set as incomplete set'); | ||
|
||
$this->assertEquals(json_encode([null]), json_encode($set), 'incomplete set is [null]'); | ||
} | ||
|
||
public function testSetAccess() | ||
{ | ||
$set = new Set(); | ||
$this->assertFalse(isset($set[0]), 'no member set by default'); | ||
$set->setComplete(false); | ||
$this->assertFalse(isset($set[0]), 'no member set by default'); | ||
|
||
$concept1 = new Concept(); | ||
$set[] = $concept1; | ||
$this->assertEquals(1, count($set), 'appended a member'); | ||
$this->assertTrue(isset($set[0]), 'member has offset 0'); | ||
|
||
$expect = [ $concept1->jsonSerialize(), null ]; | ||
$this->assertEquals(json_encode($expect), json_encode($set), 'serialize as JSON'); | ||
|
||
$concept2 = new Concept(['uri'=>'x:2']); | ||
$set['foo'] = $concept2; | ||
$this->assertEquals($concept2, $set[1], 'appended another member'); | ||
$this->assertEquals(2, count($set), 'set has two members'); | ||
$this->assertTrue($set->hasURI('x:2'), 'set contains URI'); | ||
$this->assertEquals(1, $set->findURI('x:2'), 'set contains URI at offset 1'); | ||
|
||
$concept2->uri = 'x:foo'; | ||
$this->assertEquals('x:foo', $set[1]->uri, 'append by reference'); | ||
$this->assertFalse($set->hasURI('x:2'), 'set does not contain URI'); | ||
|
||
$set[0] = $concept3 = new Concept(['uri'=>'x:3']); | ||
$set[1] = $concept4 = new Concept(['uri'=>'x:4']); | ||
$this->assertEquals($concept3, $set[0], 'modified member'); | ||
$this->assertEquals($concept4, $set[1], 'modified member'); | ||
$this->assertTrue($set->hasURI('x:3'), 'set contains URI'); | ||
|
||
$this->assertEquals(['x:3','x:4'], $set->getURIs()); | ||
|
||
$this->assertTrue($set->isValid(), 'set is valid'); | ||
$set[0]->uri = 'x:4'; | ||
$this->assertEquals(['x:4','x:4'], $set->getURIs()); | ||
$this->assertFalse($set->isValid(), 'set is not valid'); | ||
|
||
// TODO: test error handling | ||
} | ||
|
||
public function testIterator() | ||
{ | ||
$set = new Set(true); | ||
$set[] = new Concept(['uri'=>'x:foo']); | ||
$set[] = new Concept(['uri'=>'x:bar']); | ||
|
||
$members = iterator_to_array($set); | ||
$this->assertEquals(count($set), count($members)); | ||
$this->assertEquals($set[0], $members[0]); | ||
$this->assertEquals($set[1], $members[1]); | ||
|
||
$members[0]->uri = 'x:doz'; | ||
$this->assertEquals('x:doz', $set[0]->uri, 'iterate by reference'); | ||
} | ||
} |