Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ class Author {
...the following will map the XML to an object instance while reading it from the socket.

```php
use util\address\{XmlStream, ObjectOf};
use util\address\{XmlStreaming, ObjectOf};

$socket= /* ... */

$address= new XmlStream($socket->in());
$book= $address->next(new ObjectOf(Book::class, [
$stream= new XmlStreaming($socket);
$book= $stream->next(new ObjectOf(Book::class, [
'name' => fn($self) => $self->name= yield,
'author' => fn($self) => $self->author= yield new ObjectOf(Author::class, [
'name' => fn($self) => $self->name= yield ?: '(unknown author)'; }
Expand All @@ -70,23 +70,23 @@ Definitions are used to create structured data from the XML input. Here are all
Simplemost version which is given a seed value, which it can modify through the given address functions.

```php
use util\address\{XmlString, ValueOf};
use util\address\{XmlStreaming, ValueOf};

// Parse into string 'Tim Taylor'
$xml= new XmlString('<name>Tim Taylor</name>');
$name= $xml->next(new ValueOf(null, [
$stream= new XmlStreaming('<name>Tim Taylor</name>');
$name= $stream->next(new ValueOf(null, [
'.' => fn(&$self) => $self= yield,
]);

// Parse into array ['More', 'Power']
$xml= new XmlString('<tools><tool>More</tool><tool>Power</tool></tools>');
$name= $xml->next(new ValueOf([], [
$stream= new XmlStreaming('<tools><tool>More</tool><tool>Power</tool></tools>');
$name= $stream->next(new ValueOf([], [
'tool' => fn(&$self) => $self[]= yield,
]);

// Parse into map ['id' => 6100, 'name' => 'more power']
$xml= new XmlString('<tool id="6100">more power</tool>');
$book= $xml->next(new ValueOf([], [
$stream= new XmlStreaming('<tool id="6100">more power</tool>');
$book= $stream->next(new ValueOf([], [
'@id' => fn(&$self) => $self['id']= (int)yield,
'.' => fn(&$self) => $self['name']= yield,
]);
Expand All @@ -97,15 +97,15 @@ $book= $xml->next(new ValueOf([], [
Creates objects without invoking their constructors. Modifies the members directly, including non-public ones.

```php
use util\address\{XmlString, ObjectOf};
use util\address\{XmlStreaming, ObjectOf};

class Book {
public $isbn, $name;
}

// Parse into Book(isbn: '978-0552151740', name: 'A Short History...')
$xml= new XmlString('<book isbn="978-0552151740"><name>A Short History...</name></book>');
$book= $xml->next(new ObjectOf(Book::class, [
$stream= new XmlStreaming('<book isbn="978-0552151740"><name>A Short History...</name></book>');
$book= $stream->next(new ObjectOf(Book::class, [
'@isbn' => fn($self) => $self->isbn= yield,
'name' => fn($self) => $self->name= yield,
]);
Expand All @@ -116,7 +116,7 @@ $book= $xml->next(new ObjectOf(Book::class, [
Works with *record* classes, which are defined as being immutable and having an all-arg constructor. Modifies the named constructor arguments.

```php
use util\address\{XmlString, RecordOf};
use util\address\{XmlStreaming, RecordOf};

class Book {
public function __construct(private string $isbn, private string $name) { }
Expand All @@ -126,8 +126,8 @@ class Book {
}

// Parse into Book(isbn: '978-0552151740', name: 'A Short History...')
$xml= new XmlString('<book isbn="978-0552151740"><name>A Short History...</name></book>');
$book= $xml->next(new RecordOf(Book::class, [
$stream= new XmlStreaming('<book isbn="978-0552151740"><name>A Short History...</name></book>');
$book= $stream->next(new RecordOf(Book::class, [
'@isbn' => fn(&$args) => $args['isbn']= yield,
'name' => fn(&$args) => $args['name']= yield,
]);
Expand Down
106 changes: 9 additions & 97 deletions src/main/php/util/address/Address.class.php
Original file line number Diff line number Diff line change
@@ -1,113 +1,25 @@
<?php namespace util\address;

use IteratorAggregate, Traversable;
use util\NoSuchElementException;
use Iterator;

/**
* Base class for all XML inputs
*
* @test xp://util.address.unittest.AddressTest
* @deprecated Inherit from util.address.Streaming instead!
*/
abstract class Address implements IteratorAggregate {
private $iterator;
abstract class Address extends Streaming {

/**
* Gets iterator
* Returns a stream
*
* @param bool $rewind Whether to initially rewind the iterator
* @return Traversable
* @return io.streams.InputStream
*/
public function getIterator($rewind= false): Traversable {
if (null === $this->iterator) {
$this->iterator= new XmlIterator($this->stream());
if ($rewind) {
$this->iterator->rewind();
}
}
return $this->iterator;
}
protected function stream() { return $this->stream; }

/**
* Iterate over pointers
*
* @param ?string $filter
* @return iterable
*/
public function pointers($filter= null) {
$it= $this->getIterator(true);
$pointer= new Pointer($this);

if (null === $filter) {
while ($it->valid()) {
yield $it->key() => $pointer;
$it->next();
}
} else {
while ($it->valid()) {
$filter === $it->key() && yield $filter => $pointer;
$it->next();
}
}
}

/** @return io.streams.InputStream */
protected abstract function stream();

/**
* Reset this input
*
* @throws lang.IllegalStateException if underlying source is not seekable
*/
public function reset() {
$this->getIterator()->rewind();
}

/**
* Checks whether there are more elements in this iteration
*
* @return bool
*/
public function valid() {
return $this->getIterator(true)->valid();
}

/** @return string */
public function path() {
$it= $this->getIterator(true);
return $it->valid() ? $it->key() : null;
}

/**
* Returns the current value according to the given definition
*
* @param util.address.Definition $definition
* @return var
* @throws util.NoSuchElementException if there are no more elements
*/
public function value(Definition $definition= null) {
$it= $this->getIterator(true);
if ($it->valid()) {
return null === $definition ? $it->current() : $it->value($definition, $this, true);
}

throw new NoSuchElementException('No more elements in iterator');
}

/**
* Returns the next value according to the given definition
*
* @param util.address.Definition $definition
* @return var
* @throws util.NoSuchElementException if there are no more elements
* Creates an iterator. Default implementation is to return an
* `XmlIterator` instance for BC reasons.
*/
public function next(Definition $definition= null) {
$it= $this->getIterator(true);
if ($it->valid()) {
$value= null === $definition ? $it->current() : $it->value($definition, $this, false);
$it->next();
return $value;
}
public function iterator(): Iterator { return new XmlIterator($this->stream()); }

throw new NoSuchElementException('No more elements in iterator');
}
}
20 changes: 10 additions & 10 deletions src/main/php/util/address/Iteration.class.php
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
<?php namespace util\address;

class Iteration {
private $address;
private $streaming;
public $tokens= [];

/**
* Creates an iteration
*
* @param util.address.Address $address
* @param util.address.Streaming $streaming
* @param string $base
*/
public function __construct(Address $address) {
$this->address= $address;
public function __construct(Streaming $streaming) {
$this->streaming= $streaming;
}

/** @return util.address.Address */
public function address() { return $this->address; }
/** @return util.address.Streaming */
public function streaming() { return $this->streaming; }

/** @return bool */
public function valid() { return $this->address->valid(); }
public function valid() { return $this->streaming->valid(); }

/** @return string */
public function path() { return $this->address->path(); }
public function path() { return $this->streaming->path(); }

/**
* Returns the next value according to the given definition.
Expand All @@ -30,10 +30,10 @@ public function path() { return $this->address->path(); }
* @return var
*/
public function next(Definition $definition= null) {
$it= $this->address->getIterator(true);
$it= $this->streaming->getIterator(true);
$this->tokens[]= $it->token;

$value= null === $definition ? $it->current() : $it->value($definition, $this->address, false);
$value= null === $definition ? $it->current() : $it->value($definition, $this->streaming, false);
$it->next();
return $value;
}
Expand Down
16 changes: 8 additions & 8 deletions src/main/php/util/address/Pointer.class.php
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<?php namespace util\address;

class Pointer {
private $address;
private $streaming;

/** Creates a pointer to a given address */
public function __construct(Address $address) {
$this->address= $address;
/** Creates a pointer to a given streaming */
public function __construct(streaming $streaming) {
$this->streaming= $streaming;
}

/** @return util.address.Address */
public function address() { return $this->address; }
/** @return util.address.Streaming */
public function streaming() { return $this->streaming; }

/**
* Returns the current value according to the given definition
Expand All @@ -19,7 +19,7 @@ public function address() { return $this->address; }
* @throws util.NoSuchElementException if there are no more elements
*/
public function value(Definition $definition= null) {
$it= $this->address->getIterator(true);
return null === $definition ? $it->current() : $it->value($definition, $this->address, true);
$it= $this->streaming->getIterator(true);
return null === $definition ? $it->current() : $it->value($definition, $this->streaming, true);
}
}
Loading