Skip to content

Commit

Permalink
Implemented JSKOS Set
Browse files Browse the repository at this point in the history
  • Loading branch information
nichtich committed Aug 3, 2016
1 parent 1e3e838 commit 893905f
Show file tree
Hide file tree
Showing 2 changed files with 296 additions and 0 deletions.
201 changes: 201 additions & 0 deletions src/Set.php
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;
}
}
95 changes: 95 additions & 0 deletions tests/SetTest.php
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');
}
}

0 comments on commit 893905f

Please sign in to comment.