/**
* Recursively decodes the WBXML from input stream. This means that if this
* message contains complex types (like Appointment.Recuurence for example)
* the sub-objects are auto-instantiated and decoded as well. Places the
* decoded objects in the local properties array.
*
* @param Horde_ActiveSync_Wbxml_Decoder The stream decoder
*
* @throws Horde_ActiveSync_Exception
*/
public function decodeStream(Horde_ActiveSync_Wbxml_Decoder &$decoder)
{
while (1) {
$entity = $decoder->getElement();
if ($entity[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG) {
if (!($entity[Horde_ActiveSync_Wbxml::EN_FLAGS] & Horde_ActiveSync_Wbxml::EN_FLAGS_CONTENT)) {
$map = $this->_mapping[$entity[Horde_ActiveSync_Wbxml::EN_TAG]];
if (!isset($map[self::KEY_TYPE])) {
$this->{$map}[self::KEY_ATTRIBUTE] = '';
} elseif ($map[self::KEY_TYPE] == self::TYPE_DATE || $map[self::KEY_TYPE] == self::TYPE_DATE_DASHES) {
$this->{$map}[self::KEY_ATTRIBUTE] = '';
}
continue;
}
// Found start tag
if (!isset($this->_mapping[$entity[Horde_ActiveSync_Wbxml::EN_TAG]])) {
$this->_logger->err('Tag ' . $entity[Horde_ActiveSync_Wbxml::EN_TAG] . ' unexpected in type XML type ' . get_class($this));
throw new Horde_ActiveSync_Exception('Unexpected tag');
} else {
$map = $this->_mapping[$entity[Horde_ActiveSync_Wbxml::EN_TAG]];
if (isset($map[self::KEY_VALUES])) {
// Handle arrays of attribute values
while (1) {
if (!$decoder->getElementStartTag($map[self::KEY_VALUES])) {
break;
}
if (isset($map[self::KEY_TYPE])) {
$class = $map[self::KEY_TYPE];
$decoded = new $class(array('protocolversion' => $this->_version, 'logger' => $this->_logger));
$decoded->decodeStream($decoder);
} else {
$decoded = $decoder->getElementContent();
}
if (!isset($this->{$map}[self::KEY_ATTRIBUTE])) {
$this->{$map}[self::KEY_ATTRIBUTE] = array($decoded);
} else {
$this->{$map[self::KEY_ATTRIBUTE]}[] = $decoded;
}
if (!$decoder->getElementEndTag()) {
throw new Horde_ActiveSync_Exception('Missing expected wbxml end tag');
}
}
if (!$decoder->getElementEndTag()) {
return false;
}
} else {
// Handle a simple attribute value
if (isset($map[self::KEY_TYPE])) {
// Complex type, decode recursively
if ($map[self::KEY_TYPE] == self::TYPE_DATE || $map[self::KEY_TYPE] == self::TYPE_DATE_DASHES) {
$decoded = $this->_parseDate($decoder->getElementContent());
if (!$decoder->getElementEndTag()) {
throw new Horde_ActiveSync_Exception('Missing expected wbxml end tag');
}
} elseif ($map[self::KEY_TYPE] == self::TYPE_HEX) {
$decoded = self::hex2bin($decoder->getElementContent());
if (!$decoder->getElementEndTag()) {
throw new Horde_ActiveSync_Exception('Missing expected wbxml end tag');
}
} else {
$class = $map[self::KEY_TYPE];
$subdecoder = new $class(array('protocolversion' => $this->_version, 'logger' => $this->_logger));
if ($subdecoder->decodeStream($decoder) === false) {
throw new Horde_ActiveSync_Exception('Missing expected wbxml end tag');
}
$decoded = $subdecoder;
if (!$decoder->getElementEndTag()) {
$this->_logger->err('No end tag for ' . $entity[Horde_ActiveSync_Wbxml::EN_TAG]);
throw new Horde_ActiveSync_Exception('Missing expected wbxml end tag');
}
}
} else {
// Simple type, just get content
$decoded = $decoder->getElementContent();
if ($decoded === false) {
$this->_logger->err('Unable to get content for ' . $entity[Horde_ActiveSync_Wbxml::EN_TAG]);
}
if (!$decoder->getElementEndTag()) {
$this->_logger->err('Unable to get end tag for ' . $entity[Horde_ActiveSync_Wbxml::EN_TAG]);
throw new Horde_ActiveSync_Exception('Missing expected wbxml end tag');
}
}
// $decoded now contains data object (or string)
$this->{$map}[self::KEY_ATTRIBUTE] = $decoded;
}
}
} elseif ($entity[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG) {
$decoder->_ungetElement($entity);
break;
} else {
$this->_logger->err('Unexpected content in type');
break;
}
}
}