' . "\n" . $this->renderTable($this->table);
    }
    /**
     * Converts the given data table to an array
     *
     * @param DataTable|DataTable/Map $table  data table to convert
     * @return array
     */
    protected function getArrayFromDataTable($table)
    {
        if (is_array($table)) {
            return $table;
        }
        $renderer = new Php();
        $renderer->setRenderSubTables($this->isRenderSubtables());
        $renderer->setSerialize(false);
        $renderer->setTable($table);
        $renderer->setHideIdSubDatableFromResponse($this->hideIdSubDatatable);
        return $renderer->flatRender();
    }
    /**
     * Computes the output for the given data table
     *
     * @param DataTable|DataTable/Map $table
     * @param bool $returnOnlyDataTableXml
     * @param string $prefixLines
     * @return array|string
     * @throws Exception
     */
    protected function renderTable($table, $returnOnlyDataTableXml = false, $prefixLines = '')
    {
        $array = $this->getArrayFromDataTable($table);
        if ($table instanceof Map) {
            $out = $this->renderDataTableMap($table, $array, $prefixLines);
            if ($returnOnlyDataTableXml) {
                return $out;
            }
            $out = "\n$out";
            return $out;
        }
        // integer value of ZERO is a value we want to display
        if ($array != 0 && empty($array)) {
            if ($returnOnlyDataTableXml) {
                throw new Exception("Illegal state, what xml shall we return?");
            }
            $out = "";
            return $out;
        }
        if ($table instanceof Simple) {
            if (is_array($array)) {
                $out = $this->renderDataTableSimple($array);
            } else {
                $out = $array;
            }
            if ($returnOnlyDataTableXml) {
                return $out;
            }
            if (is_array($array)) {
                $out = "\n" . $out . "";
            } else {
                $value = self::formatValueXml($out);
                if ($value === '') {
                    $out = "";
                } else {
                    $out = "" . $value . "";
                }
            }
            return $out;
        }
        if ($table instanceof DataTable) {
            $out = $this->renderDataTable($array);
            if ($returnOnlyDataTableXml) {
                return $out;
            }
            $out = "\n$out";
            return $out;
        }
        if (is_array($array)) {
            $out = $this->renderArray($array, $prefixLines . "\t");
            if ($returnOnlyDataTableXml) {
                return $out;
            }
            return "\n$out";
        }
    }
    /**
     * Renders an array as XML.
     *
     * @param array $array The array to render.
     * @param string $prefixLines The string to prefix each line in the output.
     * @return string
     */
    private function renderArray($array, $prefixLines)
    {
        $isAssociativeArray = Piwik::isAssociativeArray($array);
        // check if array contains arrays, and if not wrap the result in an extra  element
        // (only check if this is the root renderArray call)
        // NOTE: this is for backwards compatibility. before, array's were added to a new DataTable.
        // if the array had arrays, they were added as multiple rows, otherwise it was treated as
        // one row. removing will change API output.
        $wrapInRow = $prefixLines === "\t"
            && self::shouldWrapArrayBeforeRendering($array, $wrapSingleValues = false, $isAssociativeArray);
        // render the array
        $result = "";
        if ($wrapInRow) {
            $result .= "$prefixLines\n";
            $prefixLines .= "\t";
        }
        foreach ($array as $key => $value) {
            // based on the type of array & the key, determine how this node will look
            if ($isAssociativeArray) {
                if (strpos($key, '=') !== false) {
                    list($keyAttributeName, $key) = explode('=', $key, 2);
                    $prefix = "";
                    $suffix = "
";
                    $emptyNode = "";
                } elseif (!self::isValidXmlTagName($key)) {
                    $prefix = "";
                    $suffix = "
";
                    $emptyNode = "";
                } else {
                    $prefix = "<$key>";
                    $suffix = "$key>";
                    $emptyNode = "<$key />";
                }
            } else {
                $prefix = "";
                $suffix = "
";
                $emptyNode = "|
";
            }
            // render the array item
            if (is_array($value)) {
                $result .= $prefixLines . $prefix . "\n";
                $result .= $this->renderArray($value, $prefixLines . "\t");
                $result .= $prefixLines . $suffix . "\n";
            } elseif ($value instanceof DataTable
                || $value instanceof Map
            ) {
                if ($value->getRowsCount() == 0) {
                    $result .= $prefixLines . $emptyNode . "\n";
                } else {
                    $result .= $prefixLines . $prefix . "\n";
                    if ($value instanceof Map) {
                        $result .= $this->renderDataTableMap($value, $this->getArrayFromDataTable($value), $prefixLines);
                    } elseif ($value instanceof Simple) {
                        $result .= $this->renderDataTableSimple($this->getArrayFromDataTable($value), $prefixLines);
                    } else {
                        $result .= $this->renderDataTable($this->getArrayFromDataTable($value), $prefixLines);
                    }
                    $result .= $prefixLines . $suffix . "\n";
                }
            } else {
                $xmlValue = self::formatValueXml($value);
                if (strlen($xmlValue) != 0) {
                    $result .= $prefixLines . $prefix . $xmlValue . $suffix . "\n";
                } else {
                    $result .= $prefixLines . $emptyNode . "\n";
                }
            }
        }
        if ($wrapInRow) {
            $result .= substr($prefixLines, 0, strlen($prefixLines) - 1) . "
\n";
        }
        return $result;
    }
    /**
     * Computes the output for the given data table array
     *
     * @param Map $table
     * @param array $array
     * @param string $prefixLines
     * @return string
     */
    protected function renderDataTableMap($table, $array, $prefixLines = "")
    {
        // CASE 1
        //array
        //  'day1' => string '14' (length=2)
        //  'day2' => string '6' (length=1)
        $firstTable = current($array);
        if (!is_array($firstTable)) {
            $xml = '';
            $nameDescriptionAttribute = $table->getKeyName();
            foreach ($array as $valueAttribute => $value) {
                if (empty($value)) {
                    $xml .= $prefixLines . "\t\n";
                } elseif ($value instanceof Map) {
                    $out = $this->renderTable($value, true);
                    //TODO somehow this code is not tested, cover this case
                    $xml .= "\t\n$out\n";
                } else {
                    $xml .= $prefixLines . "\t" . self::formatValueXml($value) . "\n";
                }
            }
            return $xml;
        }
        $subTables = $table->getDataTables();
        $firstTable = current($subTables);
        // CASE 2
        //array
        //  'day1' =>
        //    array
        //      'nb_uniq_visitors' => string '18'
        //      'nb_visits' => string '101'
        //  'day2' =>
        //    array
        //      'nb_uniq_visitors' => string '28'
        //      'nb_visits' => string '11'
        if ($firstTable instanceof Simple) {
            $xml = '';
            $nameDescriptionAttribute = $table->getKeyName();
            foreach ($array as $valueAttribute => $dataTableSimple) {
                if (count($dataTableSimple) == 0) {
                    $xml .= $prefixLines . "\t\n";
                } else {
                    if (is_array($dataTableSimple)) {
                        $dataTableSimple = "\n" . $this->renderDataTableSimple($dataTableSimple, $prefixLines . "\t") . $prefixLines . "\t";
                    }
                    $xml .= $prefixLines . "\t" . $dataTableSimple . "\n";
                }
            }
            return $xml;
        }
        // CASE 3
        //array
        //  'day1' =>
        //    array
        //      0 =>
        //        array
        //          'label' => string 'phpmyvisites'
        //          'nb_uniq_visitors' => int 11
        //          'nb_visits' => int 13
        //      1 =>
        //        array
        //          'label' => string 'phpmyvisits'
        //          'nb_uniq_visitors' => int 2
        //          'nb_visits' => int 2
        //  'day2' =>
        //    array
        //      0 =>
        //        array
        //          'label' => string 'piwik'
        //          'nb_uniq_visitors' => int 121
        //          'nb_visits' => int 130
        //      1 =>
        //        array
        //          'label' => string 'piwik bis'
        //          'nb_uniq_visitors' => int 20
        //          'nb_visits' => int 120
        if ($firstTable instanceof DataTable) {
            $xml = '';
            $nameDescriptionAttribute = $table->getKeyName();
            foreach ($array as $keyName => $arrayForSingleDate) {
                $dataTableOut = $this->renderDataTable($arrayForSingleDate, $prefixLines . "\t");
                if (empty($dataTableOut)) {
                    $xml .= $prefixLines . "\t\n";
                } else {
                    $xml .= $prefixLines . "\t\n";
                    $xml .= $dataTableOut;
                    $xml .= $prefixLines . "\t\n";
                }
            }
            return $xml;
        }
        if ($firstTable instanceof Map) {
            $xml = '';
            $tables = $table->getDataTables();
            $nameDescriptionAttribute = $table->getKeyName();
            foreach ($tables as $valueAttribute => $tableInArray) {
                $out = $this->renderTable($tableInArray, true, $prefixLines . "\t");
                $xml .= $prefixLines . "\t\n" . $out . $prefixLines . "\t\n";
            }
            return $xml;
        }
        return '';
    }
    /**
     * Computes the output for the given data array
     *
     * @param array $array
     * @param string $prefixLine
     * @return string
     */
    protected function renderDataTable($array, $prefixLine = "")
    {
        $columnsHaveInvalidChars = $this->areTableLabelsInvalidXmlTagNames(reset($array));
        $out = '';
        foreach ($array as $rowId => $row) {
            if (!is_array($row)) {
                $value = self::formatValueXml($row);
                if (strlen($value) == 0) {
                    $out .= $prefixLine . "\t\t<$rowId />\n";
                } else {
                    $out .= $prefixLine . "\t\t<$rowId>" . $value . "$rowId>\n";
                }
                continue;
            }
            // Handing case idgoal=7, creating a new array for that one
            $rowAttribute = '';
            if (strstr($rowId, '=') !== false) {
                $rowAttribute = explode('=', $rowId);
                $rowAttribute = " " . $rowAttribute[0] . "='" . $rowAttribute[1] . "'";
            }
            $out .= $prefixLine . "\t";
            if (count($row) === 1
                && key($row) === 0
            ) {
                $value = self::formatValueXml(current($row));
                $out .= $prefixLine . $value;
            } else {
                $out .= "\n";
                foreach ($row as $name => $value) {
                    // handle the recursive dataTable case by XML outputting the recursive table
                    if (is_array($value)) {
                        $value = "\n" . $this->renderDataTable($value, $prefixLine . "\t\t");
                        $value .= $prefixLine . "\t\t";
                    } else {
                        $value = self::formatValueXml($value);
                    }
                    list($tagStart, $tagEnd) = $this->getTagStartAndEndFor($name, $columnsHaveInvalidChars);
                    if (strlen($value) == 0) {
                        $out .= $prefixLine . "\t\t<$tagStart />\n";
                    } else {
                        $out .= $prefixLine . "\t\t<$tagStart>" . $value . "$tagEnd>\n";
                    }
                }
                $out .= "\t";
            }
            $out .= $prefixLine . "
\n";
        }
        return $out;
    }
    /**
     * Computes the output for the given data array (representing a simple data table)
     *
     * @param $array
     * @param string $prefixLine
     * @return string
     */
    protected function renderDataTableSimple($array, $prefixLine = "")
    {
        if (!is_array($array)) {
            $array = array('value' => $array);
        }
        $columnsHaveInvalidChars = $this->areTableLabelsInvalidXmlTagNames($array);
        $out = '';
        foreach ($array as $keyName => $value) {
            $xmlValue = self::formatValueXml($value);
            list($tagStart, $tagEnd) = $this->getTagStartAndEndFor($keyName, $columnsHaveInvalidChars);
            if (strlen($xmlValue) == 0) {
                $out .= $prefixLine . "\t<$tagStart />\n";
            } else {
                $out .= $prefixLine . "\t<$tagStart>" . $xmlValue . "$tagEnd>\n";
            }
        }
        return $out;
    }
    /**
     * Returns true if a string is a valid XML tag name, false if otherwise.
     *
     * @param string $str
     * @return bool
     */
    private static function isValidXmlTagName($str)
    {
        static $validTagRegex = null;
        if ($validTagRegex === null) {
            $invalidTagChars = "!\"#$%&'()*+,\\/;<=>?@[\\]\\\\^`{|}~";
            $invalidTagStartChars = $invalidTagChars . "\\-.0123456789";
            $validTagRegex = "/^[^" . $invalidTagStartChars . "][^" . $invalidTagChars . "]*$/";
        }
        $result = preg_match($validTagRegex, $str);
        return !empty($result);
    }
    private function areTableLabelsInvalidXmlTagNames($rowArray)
    {
        if (!empty($rowArray)) {
            foreach ($rowArray as $name => $value) {
                if (!self::isValidXmlTagName($name)) {
                    return true;
                }
            }
        }
        return false;
    }
    private function getTagStartAndEndFor($keyName, $columnsHaveInvalidChars)
    {
        if ($columnsHaveInvalidChars) {
            $tagStart = "col name=\"" . self::formatValueXml($keyName) . "\"";
            $tagEnd = "col";
        } else {
            $tagStart = $tagEnd = $keyName;
        }
        return array($tagStart, $tagEnd);
    }
}