diff --git a/backend/lib/View/XML.php b/backend/lib/View/XML.php index 4e21fce..7f427ac 100644 --- a/backend/lib/View/XML.php +++ b/backend/lib/View/XML.php @@ -24,7 +24,6 @@ namespace Volkszaehler\View; use Volkszaehler\Interpreter; - use Volkszaehler\View\HTTP; use Volkszaehler\Util; use Volkszaehler\Model; @@ -36,124 +35,206 @@ use Volkszaehler\Model; * @package default */ class XML extends View { - protected $xmlDoc = NULL; - protected $xmlRoot = NULL; - protected $xmlChannels = NULL; - protected $xmlAggregators = NULL; - protected $xmlDatas = NULL; + /** + * @var DOMDocument contains the XML tree + */ + protected $xmlDoc; + /** + * @var DOMNode reference to the XML root node + */ + protected $xmlRoot; + + /** + * Constructor + * + * @param HTTP\Request $request + * @param HTTP\Response $response + */ public function __construct(HTTP\Request $request, HTTP\Response $response) { parent::__construct($request, $response); $this->xmlDoc = new \DOMDocument('1.0', 'UTF-8'); $this->xmlRoot = $this->xmlDoc->createElement('volkszaehler'); - $this->xmlDoc->appendChild($this->xmlRoot); - $this->xmlRoot->setAttribute('version', VZ_VERSION); - $this->xmlRoot->setAttribute('source', 'volkszaehler.org'); - $this->response->setHeader('Content-type', 'application/xml; charset=UTF-8'); + $this->xmlDoc->appendChild($this->xmlRoot); } - protected function addData(Interpreter\Interpreter $interpreter) { - $data = $interpreter->getValues(); - - $xmlData = $this->xmlDoc->createElement('data'); - - foreach ($data as $reading) { - $xmlReading = $this->xmlDoc->createElement('reading'); - - $xmlReading->setAttribute('timestamp', $reading[0]); // hardcoded data fields for performance optimization - $xmlReading->setAttribute('value', $reading[1]); - $xmlReading->setAttribute('count', $reading[2]); - - $xmlData->appendChild($xmlReading); - } - - if (!isset($this->xmlDatas)) { - $this->xmlDatas = $this->xmlDoc->createElement('datas'); - $this->xmlRoot->appendChild($this->xmlDatas); - } - - $this->xmlDatas->appendChild($xmlData); + /** + * Process, encode and print output + * + * @return string the output + */ + protected function render() { + $this->response->setHeader('Content-type', 'application/xml; charset=UTF-8'); + + echo $this->xmlDoc->saveXML(); } - public function addChannel(Model\Channel $channel) { - $xmlChannel = $this->xmlDoc->createElement('channel'); - $xmlChannel->setAttribute('uuid', $channel->getUuid()); - - $xmlChannel->appendChild($this->xmlDoc->createElement('indicator', $channel->getIndicator())); - $xmlChannel->appendChild($this->xmlDoc->createElement('unit', $channel->getUnit())); - $xmlChannel->appendChild($this->xmlDoc->createElement('name', $channel->getName())); - $xmlChannel->appendChild($this->xmlDoc->createElement('description', $channel->getDescription())); - $xmlChannel->appendChild($this->xmlDoc->createElement('resolution', (int) $channel->getResolution())); - $xmlChannel->appendChild($this->xmlDoc->createElement('cost', (float) $channel->getCost())); - - if (!isset($this->xmlChannels)) { - $this->xmlChannels = $this->xmlDoc->createElement('channels'); - $this->xmlRoot->appendChild($this->xmlChannels); + /** + * Add Entity to output queue + * + * @param Model\Entity $entity + */ + protected function addEntity(Model\Entity $entity) { + if ($entity instanceof Model\Aggregator) { + $this->xmlRoot->appendChild($this->convertAggregator($entity)); + } + else { + $this->xmlRoot->appendChild($this->convertEntity($entity)); } - - $this->xmlChannels->appendChild($xmlChannel); } - public function addAggregator(Model\Aggregator $aggregator, $recursive = FALSE) { - if (!isset($this->xmlAggregators)) { - $this->xmlAggregators = $this->xmlDoc->createElement('groups'); - $this->xmlRoot->appendChild($this->xmlAggregators); + /** + * Converts entity to DOMElement + * + * @param Model\Entity $entity + * @return DOMElement + */ + protected function convertEntity(Model\Entity $entity) { + $xmlEntity = $this->xmlDoc->createElement('entity'); + + $xmlEntity->appendChild($this->xmlDoc->createElement('uuid', $entity->getUuid())); + $xmlEntity->appendChild($this->xmlDoc->createElement('type', $entity->getType())); + + foreach ($entity->getProperties() as $key => $value) { + $xmlEntity->appendChild($this->xmlDoc->createElement($key, $value)); } - $this->xmlAggregators->appendChild($this->toXml($aggregator, $recursive)); + return $xmlEntity; } - public function toXml(Model\Aggregator $aggregator, $recursive = FALSE) { - $xmlAggregator = $this->xmlDoc->createElement('group'); - $xmlAggregator->setAttribute('uuid', $aggregator->getUuid()); - $xmlAggregator->appendChild($this->xmlDoc->createElement('name', $aggregator->getName())); - $xmlAggregator->appendChild($this->xmlDoc->createElement('description', $aggregator->getDescription())); + /** + * Converts aggregator to DOMElement + * + * @param Model\Aggregator $aggregator + * @return DOMElement + */ + protected function convertAggregator(Model\Aggregator $aggregator, $recursive = FALSE) { + $xmlAggregator = $this->convertEntity($aggregator); + $xmlChildren = $this->xmlDoc->createElement('children'); - if ($recursive) { - $xmlChildren = $this->xmlDoc->createElement('children'); - - foreach ($aggregator->getChildren() as $child) { - $xmlChildren->appendChild($this->toXml($child, $recursive)); + foreach ($aggregator->getChildren() as $entity) { + if ($entity instanceof Model\Channel) { + $xmlChildren->appendChild($this->convertEntity($entity)); + } + elseif ($entity instanceof Model\Aggregator) { + $xmlChildren->appendChild($this->convertAggregator($entity)); } - - $xmlAggregator->appendChild($xmlChildren); } + $xmlAggregator->appendChild($xmlChildren); return $xmlAggregator; } - public function addDebug(Util\Debug $debug) { + /** + * Add debugging information include queries and messages to output queue + * + * @param Util\Debug $debug + */ + protected function addDebug(Util\Debug $debug) { $xmlDebug = $this->xmlDoc->createElement('debug'); - $xmlDebug->appendChild($this->xmlDoc->createElement('time', $debug->getExecutionTime())); - $xmlDebug->appendChild($this->xmlDoc->createElement('database', Util\Configuration::read('db.driver'))); - - // TODO add queries to xml debug - // TODO add messages to xml output + $xmlDebug->appendChild($this->convertArray($debug->getMessages(), 'messages')); + + $xmlDatabase = $this->xmlDoc->createElement('database'); + $xmlDatabase->setAttribute('driver', Util\Configuration::read('db.driver')); + $xmlDatabase->appendChild($this->convertArray($debug->getQueries(), 'queries')); + $xmlDebug->appendChild($xmlDatabase); $this->xmlRoot->appendChild($xmlDebug); } + /** + * Add exception to output queue + * + * @param \Exception $exception + * @param boolean $debug + */ protected function addException(\Exception $exception) { $xmlException = $this->xmlDoc->createElement('exception'); + $xmlException->setAttribute('code', $exception->getCode()); $xmlException->appendChild($this->xmlDoc->createElement('message', $exception->getMessage())); $xmlException->appendChild($this->xmlDoc->createElement('line', $exception->getLine())); $xmlException->appendChild($this->xmlDoc->createElement('file', $exception->getFile())); - $xmlException->appendChild($this->fromTrace($exception->getTrace())); + $xmlException->appendChild($this->convertTrace($exception->getTrace())); $this->xmlRoot->appendChild($xmlException); } - protected function render() { - echo $this->xmlDoc->saveXML(); + /** + * Add data to output queue + * + * @param Interpreter\InterpreterInterface $interpreter + */ + protected function addData(Interpreter\InterpreterInterface $interpreter) { + $data = $interpreter->getValues($this->request->getParameter('tuples'), $this->request->getParameter('group')); + $xmlData = $this->xmlDoc->createElement('data'); + $xmlTuples = $this->xmlDoc->createElement('tuples'); + foreach ($data as $tuple) { + $xmlTuple = $this->xmlDoc->createElement('tuple'); + $xmlTuple->setAttribute('timestamp', $tuple[0]); // hardcoded data fields for performance optimization + $xmlTuple->setAttribute('value', $tuple[1]); + $xmlTuple->setAttribute('count', $tuple[2]); + $xmlTuples->appendChild($xmlTuple); + } + + $xmlData->appendChild($this->xmlDoc->createElement('uuid', $interpreter->getUuid())); + $xmlData->appendChild($this->xmlDoc->createElement('count', count($data))); + $xmlData->appendChild($this->xmlDoc->createElement('first', (isset($data[0][0])) ? $data[0][0] : NULL)); + $xmlData->appendChild($this->xmlDoc->createElement('last', (isset($data[count($data)-1][0])) ? $data[count($data)-1][0] : NULL)); + $xmlData->appendChild($this->xmlDoc->createElement('min', $interpreter->getMin())); + $xmlData->appendChild($this->xmlDoc->createElement('max', $interpreter->getMax())); + $xmlData->appendChild($this->xmlDoc->createElement('average', $interpreter->getAverage())); + $xmlData->appendChild($xmlTuples); + + $this->xmlRoot->appendChild($xmlData); } - private function fromTrace($traces) { + /** + * Converts array to DOMElement + * + * @param array the input array + * @return DOMElement + */ + protected function convertArray(array $array, $identifier = 'array') { + $xmlArray = $this->xmlDoc->createElement($identifier); + + foreach ($array as $key => $value) { + // determine tagname + if (is_numeric($key)) { + if (substr($identifier, -3) == 'ies') { + $key = substr($identifier, 0, -3) . 'y'; + } + elseif (substr($identifier, -1) == 's') { + $key = substr($identifier, 0, -1); + } + else { + $key = 'index' . $key; + } + } + + if (is_array($value)) { + $xmlArray->appendChild($this->convertArray($value, $key)); + } + else { + $xmlArray->appendChild($this->xmlDoc->createElement($key, (is_scalar($value)) ? $value : 'object')); + } + } + + return $xmlArray; + } + + /** + * Converts excpetion backtrace to DOMElement + * + * @param array backtrace + * @return DOMElement + */ + private function convertTrace(array $traces) { $xmlTraces = $this->xmlDoc->createElement('backtrace'); foreach ($traces as $step => $trace) { @@ -162,7 +243,7 @@ class XML extends View { $xmlTrace->setAttribute('step', $step); foreach ($trace as $key => $value) { - switch ($key) { + switch ($key) { case 'args': $xmlArgs = $this->xmlDoc->createElement($key); $xmlTrace->appendChild($xmlArgs); @@ -184,6 +265,18 @@ class XML extends View { return $xmlTraces; } + + /** + * Overloaded to support arrays + */ + public function add($data) { + if (is_array($data)) { + $this->xmlRoot->appendChild($this->convertArray($data)); + } + else { + parent::add($data); + } + } } ?>