Index: util/adt/Stack.class.php
===================================================================
--- util/adt/Stack.class.php (revision 6673)
+++ util/adt/Stack.class.php (working copy)
@@ -6,7 +6,8 @@
uses(
'lang.IndexOutOfBoundsException',
- 'util.NoSuchElementException'
+ 'util.NoSuchElementException',
+ 'util.adt.HashProvider'
);
/**
@@ -37,7 +38,8 @@
*/
class Stack extends Object {
var
- $_elements= array();
+ $_elements = array(),
+ $_hash = 0;
/**
* Pushes an item onto the top of the stack. Returns the element that
@@ -49,6 +51,7 @@
*/
function &push(&$object) {
array_unshift($this->_elements, $object);
+ $this->_hash+= HashProvider::hashOf($object->hashCode());
return $object;
}
@@ -63,7 +66,9 @@
if (empty($this->_elements)) {
return throw(new NoSuchElementException('Stack is empty'));
}
- return array_shift($this->_elements);
+ $element= array_shift($this->_elements);
+ $this->_hash+= HashProvider::hashOf($element->hashCode());
+ return $element;
}
/**
@@ -126,5 +131,29 @@
}
return $this->_elements[$index];
}
+
+ /**
+ * Returns a hashcode for this queue
+ *
+ * @access public
+ * @return string
+ */
+ function hashCode() {
+ return $this->_hash;
+ }
+
+ /**
+ * Returns true if this queue equals another queue.
+ *
+ * @access public
+ * @param &lang.Object cmp
+ * @return bool
+ */
+ function equals(&$cmp) {
+ return (
+ is('util.adt.Stack', $cmp) &&
+ ($this->hashCode() === $cmp->hashCode())
+ );
+ }
}
?>
Index: util/adt/DJBX33AHashImplementation.class.php
===================================================================
--- util/adt/DJBX33AHashImplementation.class.php (revision 0)
+++ util/adt/DJBX33AHashImplementation.class.php (revision 0)
@@ -0,0 +1,77 @@
+= 8; $len-= 8) {
+ $hash= (($hash << 5) + $hash) + ord($str{$offset++});
+ $hash= (($hash << 5) + $hash) + ord($str{$offset++});
+ $hash= (($hash << 5) + $hash) + ord($str{$offset++});
+ $hash= (($hash << 5) + $hash) + ord($str{$offset++});
+ $hash= (($hash << 5) + $hash) + ord($str{$offset++});
+ $hash= (($hash << 5) + $hash) + ord($str{$offset++});
+ $hash= (($hash << 5) + $hash) + ord($str{$offset++});
+ $hash= (($hash << 5) + $hash) + ord($str{$offset++});
+ }
+
+ switch ($len) {
+ case 7: $hash= (($hash << 5) + $hash) + ord($str{$offset++});
+ case 6: $hash= (($hash << 5) + $hash) + ord($str{$offset++});
+ case 5: $hash= (($hash << 5) + $hash) + ord($str{$offset++});
+ case 4: $hash= (($hash << 5) + $hash) + ord($str{$offset++});
+ case 3: $hash= (($hash << 5) + $hash) + ord($str{$offset++});
+ case 2: $hash= (($hash << 5) + $hash) + ord($str{$offset++});
+ case 1: $hash= (($hash << 5) + $hash) + ord($str{$offset++});
+ }
+ return $hash;
+ }
+
+ } implements(__FILE__, 'util.adt.HashImplementation');
+?>
Index: util/adt/HashImplementation.class.php
===================================================================
--- util/adt/HashImplementation.class.php (revision 0)
+++ util/adt/HashImplementation.class.php (revision 0)
@@ -0,0 +1,25 @@
+
Index: util/adt/Set.class.php
===================================================================
--- util/adt/Set.class.php (revision 6673)
+++ util/adt/Set.class.php (working copy)
@@ -77,5 +77,22 @@
* @return lang.Object[] objects
*/
function toArray() { }
+
+ /**
+ * Returns a hashcode for this set
+ *
+ * @access public
+ * @return string
+ */
+ function hashCode() { }
+
+ /**
+ * Returns true if this set equals another set.
+ *
+ * @access public
+ * @param &lang.Object cmp
+ * @return bool
+ */
+ function equals(&$cmp) { }
}
?>
Index: util/adt/HashTable.class.php
===================================================================
--- util/adt/HashTable.class.php (revision 0)
+++ util/adt/HashTable.class.php (revision 0)
@@ -0,0 +1,175 @@
+hashCode();
+ if (!isset($this->_buckets[$h])) {
+ $previous= NULL;
+ } else {
+ $previous= &$this->_buckets[$h][1];
+ }
+
+ $this->_buckets[$h]= array(&$key, &$value);
+ $this->_hash+= HashProvider::hashOf($h.$value->hashCode());
+ return $previous;
+ }
+
+ /**
+ * Returns the value to which this map maps the specified key.
+ * Returns NULL if the map contains no mapping for this key.
+ *
+ * @access public
+ * @param &lang.Object key
+ * @return &lang.Object the value associated with the key
+ */
+ function &get(&$key) {
+ $h= $key->hashCode();
+ if (!isset($this->_buckets[$h])) return NULL;
+
+ return $this->_buckets[$h][1];
+ }
+
+ /**
+ * Removes the mapping for this key from this map if it is present.
+ * Returns the value to which the map previously associated the key,
+ * or null if the map contained no mapping for this key.
+ *
+ * @access public
+ * @param &lang.Object key
+ * @return &lang.Object the previous value associated with the key
+ */
+ function &remove(&$key) {
+ $h= $key->hashCode();
+ if (!isset($this->_buckets[$h])) {
+ $previous= NULL;
+ } else {
+ $previous= &$this->_buckets[$h][1];
+ $this->_hash-= HashProvider::hashOf($h.$previous->hashCode());
+ unset($this->_buckets[$h]);
+ }
+
+ return $previous;
+ }
+
+ /**
+ * Removes all mappings from this map.
+ *
+ * @access public
+ */
+ function clear() {
+ $this->_buckets= array();
+ $this->_hash= 0;
+ }
+
+ /**
+ * Returns the number of key-value mappings in this map
+ *
+ * @access public
+ */
+ function size() {
+ return sizeof($this->_buckets);
+ }
+
+ /**
+ * Returns true if this map contains no key-value mappings.
+ *
+ * @access public
+ */
+ function isEmpty() {
+ return empty($this->_buckets);
+ }
+
+ /**
+ * Returns true if this map contains a mapping for the specified key.
+ *
+ * @access public
+ * @param &lang.Object key
+ * @return bool
+ */
+ function containsKey(&$key) {
+ return isset($this->_buckets[$key->hashCode()]);
+ }
+
+ /**
+ * Returns true if this map maps one or more keys to the specified value.
+ *
+ * @access public
+ * @param &lang.Object value
+ * @return bool
+ */
+ function containsValue(&$value) {
+ foreach (array_keys($this->_buckets) as $key) {
+ if ($this->_buckets[$key][1]->equals($value)) return TRUE;
+ }
+ return FALSE;
+ }
+
+ /**
+ * Returns a hashcode for this map
+ *
+ * @access public
+ * @return string
+ */
+ function hashCode() {
+ return $this->_hash;
+ }
+
+ /**
+ * Returns true if this map equals another map.
+ *
+ * @access public
+ * @param &lang.Object cmp
+ * @return bool
+ */
+ function equals(&$cmp) {
+ return (
+ is('util.adt.Map', $cmp) &&
+ ($this->hashCode() === $cmp->hashCode())
+ );
+ }
+
+ /**
+ * Returns a string representation of this map
+ *
+ * @access public
+ * @return string
+ */
+ function toString() {
+ $s= $this->getClassName().'['.sizeof($this->_buckets).'] {';
+ if (0 == sizeof($this->_buckets)) return $s.' }';
+
+ $s.= "\n";
+ foreach (array_keys($this->_buckets) as $key) {
+ $s.= ' '.$this->_buckets[$key][0]->toString().' => '.$this->_buckets[$key][1]->toString().",\n";
+ }
+ return substr($s, 0, -2)."\n}";
+ }
+
+ } implements(__FILE__, 'util.adt.Map');
+?>
Index: util/adt/LRUBuffer.class.php
===================================================================
--- util/adt/LRUBuffer.class.php (revision 6673)
+++ util/adt/LRUBuffer.class.php (working copy)
@@ -17,16 +17,18 @@
$size = 0;
var
- $_buf = array();
+ $_access = array(),
+ $_elements = array();
/**
* Constructor
*
* @access public
* @param int size
+ * @throws lang.IllegalArgumentException is size is not greater than zero
*/
function __construct($size) {
- $this->size= $size;
+ $this->setSize($size);
}
/**
@@ -47,48 +49,68 @@
* buffer's size has not yet been exceeded).
*
*
- * $deleted= $buf->add($key);
+ * $deleted= &$buf->add($key);
*
*
* @access public
- * @param string key
- * @return int
+ * @param &lang.Object element
+ * @return &lang.Object victim
*/
- function add($id) {
- $this->_buf[$id]= $this->microtime();
- if (sizeof($this->_buf) > $this->size) {
+ function &add(&$element) {
+ $h= $element->hashCode();
+ $this->_access[$h]= $this->microtime();
+ $this->_elements[$h]= &$element;
+
+ // Check if this buffer's size has been exceeded
+ if (sizeof($this->_access) <= $this->size) return NULL;
- // Find the position of the smallest value and delete it
- $p= array_search(min($this->_buf), $this->_buf, TRUE);
- unset($this->_buf[$p]);
- return $p;
- }
+ // Find the position of the smallest value and delete it
+ $p= array_search(min($this->_access), $this->_access, TRUE);
+ $victim= &$this->_elements[$p];
- return NULL;
+ unset($this->_access[$p]);
+ unset($this->_elements[$p]);
+
+ return $victim;
}
/**
* Update an element
*
* @access public
- * @param string id
+ * @param &lang.Object element
*/
- function update($id) {
- $this->_buf[$id]= $this->microtime();
+ function update(&$element) {
+ $this->_access[$element->hashCode()]= $this->microtime();
}
/**
- * Set Size
+ * Get number of elements currently contained in this buffer
*
* @access public
+ * @return int
+ */
+ function numElements() {
+ return sizeof($this->_access);
+ }
+
+ /**
+ * Set size
+ *
+ * @access public
* @param int size
+ * @throws lang.IllegalArgumentException is size is not greater than zero
*/
function setSize($size) {
+ if ($size <= 0) return throw(new IllegalArgumentException(
+ 'Size must be greater than zero, '.$size.' given'
+ ));
+
$this->size= $size;
}
/**
- * Get Size
+ * Get size
*
* @access public
* @return int
Index: util/adt/Map.class.php
===================================================================
--- util/adt/Map.class.php (revision 0)
+++ util/adt/Map.class.php (revision 0)
@@ -0,0 +1,110 @@
+
Index: util/adt/HashProvider.class.php
===================================================================
--- util/adt/HashProvider.class.php (revision 0)
+++ util/adt/HashProvider.class.php (revision 0)
@@ -0,0 +1,102 @@
+
+ * $hashCode= HashProvider::hashOf($string);
+ *
+ *
+ * Uses DJBX33A as default hashing implementation. To change the hashing
+ * implementation to be used, use the following:
+ *
+ * $provider= &HashProvider::getInstance();
+ * $provider->setImplementation(new MyHashImplementation());
+ *
+ *
+ * @see xp://util.adt.DJBX33AHashImplementation
+ * @see xp://util.adt.Map
+ * @purpose Hashing
+ */
+ class HashProvider extends Object {
+ var
+ $impl= NULL;
+
+ /**
+ * Static initializer. Sets DJBX33A as default hashing implementation.
+ *
+ * @model static
+ * @access public
+ */
+ function __static() {
+ $self= &HashProvider::getInstance();
+ $self->setImplementation(new DJBX33AHashImplementation());
+ }
+
+ /**
+ * Retrieve sole instance of this object
+ *
+ * @model static
+ * @access public
+ * @return &util.adt.HashProvider
+ */
+ function &getInstance() {
+ static $instance= NULL;
+
+ if (!isset($instance)) {
+ $instance= new HashProvider();
+
+ // No need to set the impl member, this block is only ever
+ // executed from the static initializer, which sets this
+ // member to the default hashing implementation.
+ }
+ return $instance;
+ }
+
+ /**
+ * Returns hash for a given string
+ *
+ * @model static
+ * @access public
+ * @param string str
+ * @return int
+ */
+ function hashOf($str) {
+ $self= &HashProvider::getInstance();
+ return $self->impl->hashOf($str);
+ }
+
+ /**
+ * Set hashing implementation
+ *
+ * @access public
+ * @param &util.adt.HashImplementation impl
+ * @throws lang.IllegalArgumentException when impl is not a HashImplementation
+ */
+ function setImplementation(&$impl) {
+ if (!is('util.adt.HashImplementation', $impl)) {
+ return throw(new IllegalArgumentException(
+ 'Implementation is not a HashImplementation, '.xp::typeOf($impl).' given'
+ ));
+ }
+ $this->impl= &$impl;
+ }
+
+ /**
+ * Get hashing implementation
+ *
+ * @access public
+ * @return &util.adt.HashImplementation
+ */
+ function &getImplementation() {
+ return $this->impl;
+ }
+ }
+?>
Index: util/adt/HashSet.class.php
===================================================================
--- util/adt/HashSet.class.php (revision 6673)
+++ util/adt/HashSet.class.php (working copy)
@@ -4,6 +4,8 @@
* $Id$
*/
+ uses('util.adt.HashProvider');
+
/**
* A set of objects
*
@@ -11,7 +13,8 @@
*/
class HashSet extends Object {
var
- $_elements= array();
+ $_elements = array(),
+ $_hash = 0;
/**
* Adds an object
@@ -24,6 +27,7 @@
$h= $object->hashCode();
if (isset($this->_elements[$h])) return FALSE;
+ $this->_hash+= HashProvider::hashOf($h);
$this->_elements[$h]= &$object;
return TRUE;
}
@@ -39,6 +43,7 @@
$h= $object->hashCode();
if (!isset($this->_elements[$h])) return FALSE;
+ $this->_hash-= HashProvider::hashOf($h);
unset($this->_elements[$h]);
return TRUE;
}
@@ -51,8 +56,7 @@
* @return bool TRUE if the set contains the specified element.
*/
function contains(&$object) {
- $h= $object->hashCode();
- return isset($this->_elements[$h]);
+ return isset($this->_elements[$object->hashCode()]);
}
/**
@@ -72,6 +76,7 @@
*/
function clear() {
$this->_elements= array();
+ $this->_hash= 0;
}
/**
@@ -98,6 +103,7 @@
if (isset($this->_elements[$h])) continue;
$result= TRUE;
+ $this->_hash+= HashProvider::hashOf($h);
$this->_elements[$h]= &$objects[$i];
}
return $result;
@@ -113,5 +119,46 @@
return array_values($this->_elements);
}
+ /**
+ * Returns a hashcode for this set
+ *
+ * @access public
+ * @return string
+ */
+ function hashCode() {
+ return $this->_hash;
+ }
+
+ /**
+ * Returns true if this set equals another set.
+ *
+ * @access public
+ * @param &lang.Object cmp
+ * @return bool
+ */
+ function equals(&$cmp) {
+ return (
+ is('util.adt.Set', $cmp) &&
+ ($this->hashCode() === $cmp->hashCode())
+ );
+ }
+
+ /**
+ * Returns a string representation of this set
+ *
+ * @access public
+ * @return string
+ */
+ function toString() {
+ $s= $this->getClassName().'['.sizeof($this->_elements).'] {';
+ if (0 == sizeof($this->_elements)) return $s.' }';
+
+ $s.= "\n";
+ foreach (array_keys($this->_elements) as $key) {
+ $s.= ' '.$this->_elements[$key]->toString().",\n";
+ }
+ return substr($s, 0, -2)."\n}";
+ }
+
} implements(__FILE__, 'util.adt.Set');
?>
Index: util/adt/Queue.class.php
===================================================================
--- util/adt/Queue.class.php (revision 6673)
+++ util/adt/Queue.class.php (working copy)
@@ -6,7 +6,8 @@
uses(
'lang.IndexOutOfBoundsException',
- 'util.NoSuchElementException'
+ 'util.NoSuchElementException',
+ 'util.adt.HashProvider'
);
/**
@@ -36,7 +37,8 @@
*/
class Queue extends Object {
var
- $_elements= array();
+ $_elements = array(),
+ $_hash = 0;
/**
* Puts an item into the queue. Returns the element that was added.
@@ -47,6 +49,7 @@
*/
function &put(&$object) {
$this->_elements[]= &$object;
+ $this->_hash+= HashProvider::hashOf($object->hashCode());
return $object;
}
@@ -61,7 +64,11 @@
if (empty($this->_elements)) {
return throw(new NoSuchElementException('Queue is empty'));
}
- return array_shift($this->_elements);
+
+ $e= &$this->_elements[0];
+ $this->_hash-= HashProvider::hashOf($e->hashCode());
+ $this->_elements= array_slice($this->_elements, 1);
+ return $e;
}
/**
@@ -121,7 +128,9 @@
function remove(&$object) {
if (-1 == ($pos= $this->search($object))) return FALSE;
+ $this->_hash-= HashProvider::hashOf($this->_elements[$pos]->hashCode());
unset($this->_elements[$pos]);
+ $this->_elements= array_values($this->_elements); // Re-index
return TRUE;
}
@@ -139,5 +148,29 @@
}
return $this->_elements[$index];
}
+
+ /**
+ * Returns a hashcode for this queue
+ *
+ * @access public
+ * @return string
+ */
+ function hashCode() {
+ return $this->_hash;
+ }
+
+ /**
+ * Returns true if this queue equals another queue.
+ *
+ * @access public
+ * @param &lang.Object cmp
+ * @return bool
+ */
+ function equals(&$cmp) {
+ return (
+ is('util.adt.Queue', $cmp) &&
+ ($this->hashCode() === $cmp->hashCode())
+ );
+ }
}
?>
Index: net/xp_framework/unittest/util/adt/StackTest.class.php
===================================================================
--- net/xp_framework/unittest/util/adt/StackTest.class.php (revision 0)
+++ net/xp_framework/unittest/util/adt/StackTest.class.php (revision 0)
@@ -0,0 +1,130 @@
+stack= &new Stack();
+ }
+
+ /**
+ * Tests the Stack is initially empty
+ *
+ * @access public
+ */
+ #[@test]
+ function initiallyEmpty() {
+ $this->assertTrue($this->stack->isEmpty());
+ }
+
+ /**
+ * Tests Stack equals its clone
+ *
+ * @access public
+ */
+ #[@test]
+ function equalsClone() {
+ $this->stack->push(new String('green'));
+ $this->assertTrue($this->stack->equals(clone($this->stack)));
+ }
+
+ /**
+ * Tests push()
+ *
+ * @access public
+ */
+ #[@test]
+ function push() {
+ $this->stack->push(new String('green'));
+ $this->assertFalse($this->stack->isEmpty());
+ $this->assertEquals(1, $this->stack->size());
+ }
+
+ /**
+ * Tests pop()
+ *
+ * @access public
+ */
+ #[@test]
+ function pop() {
+ $color= &new String('green');
+ $this->stack->push($color);
+ $this->assertEquals($color, $this->stack->pop());
+ $this->assertTrue($this->stack->isEmpty());
+ }
+
+ /**
+ * Tests peek()
+ *
+ * @access public
+ */
+ #[@test]
+ function peek() {
+ $color= &new String('green');
+ $this->stack->push($color);
+ $this->assertEquals($color, $this->stack->peek());
+ $this->assertFalse($this->stack->isEmpty());
+ }
+
+ /**
+ * Tests search()
+ *
+ * @access public
+ */
+ #[@test]
+ function search() {
+ $color= &new String('green');
+ $this->stack->push($color);
+ $this->assertEquals(0, $this->stack->search($color));
+ $this->assertEquals(-1, $this->stack->search(new String('non-existant')));
+ }
+
+ /**
+ * Tests elementAt()
+ *
+ * @access public
+ */
+ #[@test]
+ function elementAt() {
+ $this->stack->push(new String('red'));
+ $this->stack->push(new String('green'));
+ $this->stack->push(new String('blue'));
+
+ $this->assertEquals(new String('blue'), $this->stack->elementAt(0));
+ $this->assertEquals(new String('green'), $this->stack->elementAt(1));
+ $this->assertEquals(new String('red'), $this->stack->elementAt(2));
+ }
+
+ /**
+ * Tests elementAt() when given an illegal offset
+ *
+ * @access public
+ */
+ #[@test, @expect('lang.IndexOutOfBoundsException')]
+ function elementAtIllegalOffset() {
+ $this->stack->elementAt(-1);
+ }
+ }
+?>
Index: net/xp_framework/unittest/util/adt/HashTableTest.class.php
===================================================================
--- net/xp_framework/unittest/util/adt/HashTableTest.class.php (revision 0)
+++ net/xp_framework/unittest/util/adt/HashTableTest.class.php (revision 0)
@@ -0,0 +1,213 @@
+map= &new HashTable();
+ }
+
+ /**
+ * Tests the map is initially empty
+ *
+ * @access public
+ */
+ #[@test]
+ function initiallyEmpty() {
+ $this->assertTrue($this->map->isEmpty());
+ }
+
+ /**
+ * Tests map equals its clone
+ *
+ * @access public
+ */
+ #[@test]
+ function equalsClone() {
+ $this->map->put(new String('color'), new String('green'));
+ $this->assertTrue($this->map->equals(clone($this->map)));
+ }
+
+ /**
+ * Tests map equals another map with the same contents
+ *
+ * @access public
+ */
+ #[@test]
+ function equalsOtherMapWithSameContents() {
+ $other= &new HashTable();
+ $this->map->put(new String('color'), new String('green'));
+ $other->put(new String('color'), new String('green'));
+ $this->assertTrue($this->map->equals($other));
+ }
+
+ /**
+ * Tests map does not equal map with different contents
+ *
+ * @access public
+ */
+ #[@test]
+ function doesNotEqualMapWithDifferentContents() {
+ $other= &new HashTable();
+ $this->map->put(new String('color'), new String('blue'));
+ $other->put(new String('color'), new String('yellow'));
+ $this->assertFalse($this->map->equals($other));
+ }
+
+ /**
+ * Tests put()
+ *
+ * @access public
+ */
+ #[@test]
+ function put() {
+ $this->map->put(new String('color'), new String('green'));
+ $this->assertFalse($this->map->isEmpty());
+ $this->assertEquals(1, $this->map->size());
+ }
+
+ /**
+ * Tests put() returns previous value
+ *
+ * @access public
+ */
+ #[@test]
+ function putReturnsPreviousValue() {
+ $color= &new String('color');
+ $this->assertNull($this->map->put($color, new String('green')));
+ $this->assertEquals(new String('green'), $this->map->put($color, new String('red')));
+ $this->assertEquals(new String('red'), $this->map->get($color));
+ }
+
+ /**
+ * Tests get()
+ *
+ * @access public
+ */
+ #[@test]
+ function get() {
+ $this->map->put(new String('key'), new String('value'));
+ $this->assertEquals(new String('value'), $this->map->get(new String('key')));
+ }
+
+ /**
+ * Tests get() returns NULL if the list is empty
+ *
+ * @access public
+ */
+ #[@test]
+ function getReturnsNullOnEmptyList() {
+ $this->assertTrue($this->map->isEmpty());
+ $this->assertNull($this->map->get(new String('key')));
+ }
+
+ /**
+ * Tests remove()
+ *
+ * @access public
+ */
+ #[@test]
+ function remove() {
+ $this->map->put(new String('key'), new String('value'));
+ $this->map->remove(new String('key'));
+ $this->assertTrue($this->map->isEmpty());
+ }
+
+ /**
+ * Tests remove() returns previous value
+ *
+ * @access public
+ */
+ #[@test]
+ function removeReturnsPreviousValue() {
+ $this->map->put(new String('key'), new String('value'));
+ $this->assertEquals(new String('value'), $this->map->remove(new String('key')));
+ }
+
+ /**
+ * Tests containsKey() method
+ *
+ * @access public
+ */
+ #[@test]
+ function containsKey() {
+ $this->map->put(new String('key'), new String('value'));
+ $this->assertTrue($this->map->containsKey(new String('key')));
+ $this->assertFalse($this->map->containsKey(new String('non-existant-key')));
+ }
+
+ /**
+ * Tests clear() method
+ *
+ * @access public
+ */
+ #[@test]
+ function clear() {
+ $this->map->put(new String('key'), new String('value'));
+ $this->map->clear();
+ $this->assertTrue($this->map->isEmpty());
+ }
+
+ /**
+ * Tests containsValue() method
+ *
+ * @access public
+ */
+ #[@test]
+ function containsValue() {
+ $this->map->put(new String('key'), new String('value'));
+ $this->assertTrue($this->map->containsValue(new String('value')));
+ $this->assertFalse($this->map->containsValue(new String('non-existant-value')));
+ }
+
+ /**
+ * Tests toString() method
+ *
+ * @access public
+ */
+ #[@test]
+ function stringRepresentation() {
+ $this->map->put(new String('color'), new String('purple'));
+ $this->map->put(new String('price'), new String('25 USD'));
+ $this->assertEquals(
+ "util.adt.HashTable[2] {\n color => purple,\n price => 25 USD\n}",
+ $this->map->toString()
+ );
+ }
+
+ /**
+ * Tests toString() method on an empty map
+ *
+ * @access public
+ */
+ #[@test]
+ function stringRepresentationOfEmptyMap() {
+ $this->assertEquals(
+ 'util.adt.HashTable[0] { }',
+ $this->map->toString()
+ );
+ }
+ }
+?>
Index: net/xp_framework/unittest/util/adt/LRUBufferTest.class.php
===================================================================
--- net/xp_framework/unittest/util/adt/LRUBufferTest.class.php (revision 0)
+++ net/xp_framework/unittest/util/adt/LRUBufferTest.class.php (revision 0)
@@ -0,0 +1,160 @@
+buffer= &new LRUBuffer(LRUTEST_BUFFER_DEAULT_SIZE);
+ }
+
+ /**
+ * Tests the buffer is initially empty
+ *
+ * @access public
+ */
+ #[@test]
+ function initiallyEmpty() {
+ $this->assertEquals(0, $this->buffer->numElements());
+ }
+
+ /**
+ * Tests the getSize() method
+ *
+ * @access public
+ */
+ #[@test]
+ function getSize() {
+ $this->assertEquals(LRUTEST_BUFFER_DEAULT_SIZE, $this->buffer->getSize());
+ }
+
+ /**
+ * Tests the add() method
+ *
+ * @access public
+ */
+ #[@test]
+ function add() {
+ $this->buffer->add(new String('one'));
+ $this->assertEquals(1, $this->buffer->numElements());
+ }
+
+ /**
+ * Tests the add() method returns the victim
+ *
+ * @access public
+ */
+ #[@test]
+ function addReturnsVictim() {
+
+ // We should be able to add at least as many as the buffer's size
+ // elements to the LRUBuffer. Nothing should be deleted from it
+ // during this loop.
+ for ($i= 0, $s= $this->buffer->getSize(); $i < $s; $i++) {
+ if (NULL === ($victim= &$this->buffer->add(new String('item #'.$i)))) continue;
+
+ return $this->fail(
+ 'Victim '.xp::stringOf($victim).' when inserting item #'.($i + 1).'/'.$s,
+ $victim,
+ NULL
+ );
+ }
+
+ // The LRUBuffer is now "full". Next time we add something, the
+ // element last recently used should be returned.
+ $this->assertEquals(
+ new String('item #0'),
+ $this->buffer->add(new String('last item'))
+ );
+ }
+
+ /**
+ * Add a specified number of strings to the buffer.
+ *
+ * @access protected
+ * @param int num
+ */
+ function addElements($num) {
+ for ($i= 0; $i < $num; $i++) {
+ $this->buffer->add(new String('item #'.$i));
+ }
+ }
+
+ /**
+ * Tests the buffer does not grow beyond the set limit
+ *
+ * @access public
+ */
+ #[@test]
+ function bufferDoesNotGrowBeyondSize() {
+ $this->addElements($this->buffer->getSize()+ 1);
+ $this->assertEquals($this->buffer->getSize(), $this->buffer->numElements());
+ }
+
+ /**
+ * Tests the update() method
+ *
+ * @access public
+ */
+ #[@test]
+ function update() {
+
+ // Fill the LRUBuffer until its size is reached
+ $this->addElements($this->buffer->getSize());
+
+ // Update the first item
+ $this->buffer->update(new String('item #0'));
+
+ // Now the second item should be chosen the victim when adding
+ // another element
+ $this->assertEquals(
+ new String('item #1'),
+ $this->buffer->add(new String('last item'))
+ );
+ }
+
+ /**
+ * Tests the setSize() method
+ *
+ * @access public
+ */
+ #[@test]
+ function setSize() {
+ $this->buffer->setSize(10);
+ $this->assertEquals(10, $this->buffer->getSize());
+ }
+
+ /**
+ * Tests the setSize() method when passed an argument <= zero
+ *
+ * @access public
+ */
+ #[@test, @expect('lang.IllegalArgumentException')]
+ function illegalSize() {
+ $this->buffer->setSize(0);
+ }
+ }
+?>
Index: net/xp_framework/unittest/util/adt/HashSetTest.class.php
===================================================================
--- net/xp_framework/unittest/util/adt/HashSetTest.class.php (revision 0)
+++ net/xp_framework/unittest/util/adt/HashSetTest.class.php (revision 0)
@@ -0,0 +1,254 @@
+set= &new HashSet();
+ }
+
+ /**
+ * Tests the set is initially empty
+ *
+ * @access public
+ */
+ #[@test]
+ function initiallyEmpty() {
+ $this->assertTrue($this->set->isEmpty());
+ }
+
+ /**
+ * Tests set equals its clone
+ *
+ * @access public
+ */
+ #[@test]
+ function equalsClone() {
+ $this->set->add(new String('green'));
+ $this->assertTrue($this->set->equals(clone($this->set)));
+ }
+
+ /**
+ * Tests set equals another set with the same contents
+ *
+ * @access public
+ */
+ #[@test]
+ function equalsOtherSetWithSameContents() {
+ $other= &new HashSet();
+ $this->set->add(new String('color'));
+ $other->add(new String('color'));
+ $this->assertTrue($this->set->equals($other));
+ }
+
+ /**
+ * Tests set does not equal set with different contents
+ *
+ * @access public
+ */
+ #[@test]
+ function doesNotEqualSetWithDifferentContents() {
+ $other= &new HashSet();
+ $this->set->add(new String('blue'));
+ $other->add(new String('yellow'));
+ $this->assertFalse($this->set->equals($other));
+ }
+
+ /**
+ * Tests add()
+ *
+ * @access public
+ */
+ #[@test]
+ function add() {
+ $this->set->add(new String('green'));
+ $this->assertFalse($this->set->isEmpty());
+ $this->assertEquals(1, $this->set->size());
+ }
+
+ /**
+ * Tests addAll()
+ *
+ * @access public
+ */
+ #[@test]
+ function addAll() {
+ $array= array(new String('one'), new String('two'), new String('three'));
+ $this->set->addAll($array);
+ $this->assertFalse($this->set->isEmpty());
+ $this->assertEquals(3, $this->set->size());
+ }
+
+ /**
+ * Tests addAll() uniques the array given
+ *
+ * @access public
+ */
+ #[@test]
+ function addAllUniques() {
+ $array= array(new String('one'), new String('one'), new String('two'));
+ $this->set->addAll($array);
+ $this->assertFalse($this->set->isEmpty());
+ $this->assertEquals(2, $this->set->size()); // String{"one"} and String{"two"}
+ }
+
+ /**
+ * Tests addAll() returns TRUE if the set changed as a result if the
+ * call, FALSE otherwise.
+ *
+ * @access public
+ */
+ #[@test]
+ function addAllReturnsWhetherSetHasChanged() {
+ $array= array(new String('caffeine'), new String('nicotine'));
+ $this->assertTrue($this->set->addAll($array));
+ $this->assertFalse($this->set->addAll($array));
+ $this->assertFalse($this->set->addAll(array(new String('caffeine'))));
+ $this->assertFalse($this->set->addAll(array()));
+ }
+
+ /**
+ * Tests contains() method
+ *
+ * @access public
+ */
+ #[@test]
+ function contains() {
+ $this->set->add(new String('key'));
+ $this->assertTrue($this->set->contains(new String('key')));
+ $this->assertFalse($this->set->contains(new String('non-existant-key')));
+ }
+
+ /**
+ * Tests add() returns TRUE if the set did not already contain the
+ * given element, FALSE otherwise
+ *
+ * @access public
+ */
+ #[@test]
+ function addSameValueTwice() {
+ $color= &new String('green');
+ $this->assertTrue($this->set->add($color));
+ $this->assertFalse($this->set->add($color));
+ }
+
+ /**
+ * Tests remove()
+ *
+ * @access public
+ */
+ #[@test]
+ function remove() {
+ $this->set->add(new String('key'));
+ $this->assertTrue($this->set->remove(new String('key')));
+ $this->assertTrue($this->set->isEmpty());
+ }
+
+ /**
+ * Tests remove() returns FALSE when given object cannot be
+ * contained in the set (because the set is empty)
+ *
+ * @access public
+ */
+ #[@test]
+ function removeOnEmptySet() {
+ $this->assertFalse($this->set->remove(new String('irrelevant-set-is-empty-anyway')));
+ }
+
+ /**
+ * Tests remove() returns FALSE when given object is not contained
+ * in the set.
+ *
+ * @access public
+ */
+ #[@test]
+ function removeNonExistantObject() {
+ $this->set->add(new String('key'));
+ $this->assertFalse($this->set->remove(new String('non-existant-key')));
+ }
+
+ /**
+ * Tests clear() method
+ *
+ * @access public
+ */
+ #[@test]
+ function clear() {
+ $this->set->add(new String('key'));
+ $this->set->clear();
+ $this->assertTrue($this->set->isEmpty());
+ }
+
+ /**
+ * Tests toArray() method
+ *
+ * @access public
+ */
+ #[@test]
+ function toArray() {
+ $color= &new String('red');
+ $this->set->add($color);
+ $this->assertEquals(array(&$color), $this->set->toArray());
+ }
+
+ /**
+ * Tests toArray() method
+ *
+ * @access public
+ */
+ #[@test]
+ function toArrayOnEmptySet() {
+ $this->assertEquals(array(), $this->set->toArray());
+ }
+
+ /**
+ * Tests toString() method
+ *
+ * @access public
+ */
+ #[@test]
+ function stringRepresentation() {
+ $this->set->add(new String('color'));
+ $this->set->add(new String('price'));
+ $this->assertEquals(
+ "util.adt.HashSet[2] {\n color,\n price\n}",
+ $this->set->toString()
+ );
+ }
+
+ /**
+ * Tests toString() method on an empty set
+ *
+ * @access public
+ */
+ #[@test]
+ function stringRepresentationOfEmptySet() {
+ $this->assertEquals(
+ 'util.adt.HashSet[0] { }',
+ $this->set->toString()
+ );
+ }
+ }
+?>
Index: net/xp_framework/unittest/util/adt/QueueTest.class.php
===================================================================
--- net/xp_framework/unittest/util/adt/QueueTest.class.php (revision 0)
+++ net/xp_framework/unittest/util/adt/QueueTest.class.php (revision 0)
@@ -0,0 +1,235 @@
+queue= &new Queue();
+ }
+
+ /**
+ * Tests the queue is initially empty
+ *
+ * @access public
+ */
+ #[@test]
+ function initiallyEmpty() {
+ $this->assertTrue($this->queue->isEmpty());
+ }
+
+ /**
+ * Tests queue equals its clone
+ *
+ * @access public
+ */
+ #[@test]
+ function equalsClone() {
+ $this->queue->put(new String('green'));
+ $this->assertTrue($this->queue->equals(clone($this->queue)));
+ }
+
+ /**
+ * Tests put()
+ *
+ * @access public
+ */
+ #[@test]
+ function put() {
+ $this->queue->put(new String('green'));
+ $this->assertFalse($this->queue->isEmpty());
+ $this->assertEquals(1, $this->queue->size());
+ }
+
+ /**
+ * Tests get()
+ *
+ * @access public
+ */
+ #[@test]
+ function get() {
+ $color= &new String('red');
+ $this->queue->put($color);
+ $this->assertEquals($color, $this->queue->get());
+ $this->assertTrue($this->queue->isEmpty());
+ }
+
+ /**
+ * Tests get() throws an exception when there are no more elements
+ * in the queue,
+ *
+ * @access public
+ */
+ #[@test, @expect('util.NoSuchElementException')]
+ function exceptionOnNoMoreElements() {
+ $this->queue->get();
+ }
+
+ /**
+ * Tests peek()
+ *
+ * @access public
+ */
+ #[@test]
+ function peek() {
+ $color= &new String('blue');
+ $this->queue->put($color);
+ $this->assertEquals($color, $this->queue->peek());
+ $this->assertFalse($this->queue->isEmpty());
+ }
+
+ /**
+ * Tests peek() returns NULL when there are no more elements
+ * in the queue.
+ *
+ * @access public
+ */
+ #[@test]
+ function peekReturnsNullOnNoMoreElements() {
+ $this->assertNull($this->queue->peek());
+ }
+
+ /**
+ * Tests remove()
+ *
+ * @access public
+ */
+ #[@test]
+ function remove() {
+ $color= &new String('blue');
+ $this->queue->put($color);
+ $this->queue->remove($color);
+ $this->assertTrue($this->queue->isEmpty());
+ }
+
+ /**
+ * Tests remove() returns TRUE when the element was deleted, FALSE otherwise
+ *
+ * @access public
+ */
+ #[@test]
+ function removeReturnsWhetherDeleted() {
+ $color= &new String('pink');
+ $this->queue->put($color);
+ $this->assertTrue($this->queue->remove($color));
+ $this->assertFalse($this->queue->remove(new String('purple')));
+ $this->assertTrue($this->queue->isEmpty());
+ $this->assertFalse($this->queue->remove($color));
+ $this->assertFalse($this->queue->remove(new String('purple')));
+ }
+
+ /**
+ * Tests elementAt()
+ *
+ * @access public
+ */
+ #[@test]
+ function elementAt() {
+ $this->queue->put(new String('red'));
+ $this->queue->put(new String('green'));
+ $this->queue->put(new String('blue'));
+ $this->assertEquals(new String('red'), $this->queue->elementAt(0));
+ $this->assertEquals(new String('green'), $this->queue->elementAt(1));
+ $this->assertEquals(new String('blue'), $this->queue->elementAt(2));
+ }
+
+ /**
+ * Tests iterative use
+ *
+ * Example:
+ *
+ *
+ * // Fill queue
+ * with ($q= &new Queue()); {
+ * $q->put(new String('One'));
+ * $q->put(new String('Two'));
+ * $q->put(new String('Three'));
+ * $q->put(new String('Four'));
+ * }
+ *
+ * // Empty queue
+ * while (!$q->isEmpty()) {
+ * var_dump($q->get());
+ * }
+ *
+ *
+ * @access public
+ */
+ #[@test]
+ function iterativeUse() {
+ $input= array(new String('red'), new String('green'), new String('blue'));
+
+ // Add
+ for ($i= 0, $s= sizeof($input); $i < sizeof($input); $i++) {
+ $this->queue->put($input[$i]);
+ }
+
+ // Retrieve
+ $i= 0;
+ while (!$this->queue->isEmpty()) {
+ $element= &$this->queue->get();
+
+ if (!$input[$i]->equals($element)) {
+ $this->fail('Not equal at offset #'.$i, $element, $input[$i]);
+ break;
+ }
+ $i++;
+ }
+ }
+
+ /**
+ * Tests elementAt() throws an exception in case an illegal offset
+ * is specified.
+ *
+ * @access public
+ */
+ #[@test, @expect('util.IndexOutOfBoundsException')]
+ function elementAtIllegalOffset() {
+ $this->queue->elementAt(-1);
+ }
+
+ /**
+ * Tests elementAt() throws an exception in case an out-of-bound
+ * offset is specified.
+ *
+ * @access public
+ */
+ #[@test, @expect('util.IndexOutOfBoundsException')]
+ function elementAtOffsetOutOfBounds() {
+ $this->queue->put(new String('one'));
+ $this->queue->elementAt($this->queue->size() + 1);
+ }
+
+ /**
+ * Tests elementAt() throws an exception in case the list is
+ * empty.
+ *
+ * @access public
+ */
+ #[@test, @expect('util.IndexOutOfBoundsException')]
+ function elementAtEmptyList() {
+ $this->queue->elementAt(0);
+ }
+ }
+?>