protected function process($data, $isAssoc = true, $isArray = false)
    {
        $tokens = array();

        foreach ($data as $key => $value) {
            $buffer = '';
            if ($isAssoc) {
                if ($isArray) {
                    $buffer = self::encodeString($key) . ':';
                } else {
                    $buffer = $key . ':';
                }
            }
            /**
             *
             * PHP manual says what gettype() is slow and we should use is_* functions instead. But tests says that its approx. 5-10% faster encoding using single gettype() call instead of separate calls to is_int(), is_string(), etc.
             * @var string
             */
            $valueType = gettype($value);
            switch ($valueType) {
                case 'integer':
                    $buffer .= $value;
                    break;

                case 'double':
                    $buffer .= $value . chr(OrientDBRecordDecoder::CCODE_NUM_FLOAT);
                    break;

                case 'string':
                    $buffer .= self::encodeString($value);
                    break;

                case 'boolean':
                    if ($value === true) {
                        $buffer .= 'true';
                    } else {
                        $buffer .= 'false';
                    }
                    break;

                case 'array':
                    $arrayAssoc = self::isAssoc($value);
                    if ($arrayAssoc === true) {
//                        This is assoc array, using map
                        $boundStart = chr(OrientDBRecordDecoder::CCODE_OPEN_CURLY);
                        $boundEnd = chr(OrientDBRecordDecoder::CCODE_CLOSE_CURLY);
                    } else {
//                        Array will always be encoded as a set, as 1) Orient itself converts set to list (and vice-versa too) 2) PHP lacks build-in set type
                        $boundStart = chr(OrientDBRecordDecoder::CCODE_OPEN_SQUARE);
                        $boundEnd = chr(OrientDBRecordDecoder::CCODE_CLOSE_SQUARE);
                    }
                    $buffer .= $boundStart;
                    /**
                     * @TODO Fix possible PHP fatal: Maximum function nesting level of '100' reached, aborting!
                     */
                    $buffer .= implode(',', $this->process($value, $arrayAssoc, true));
                    $buffer .= $boundEnd;
                    break;

                case 'NULL':

                    break;

                case is_a($value, 'OrientDBTypeLink'):
                    /** @var $value OrientDBTypeLink */
                    $buffer .= $value->getHash();
                    break;

                case is_a($value, 'OrientDBTypeDate'):
                    $buffer .= (string) $value;
                    break;

                case is_a($value, 'OrientDBRecord'):
                    $buffer .= chr(OrientDBRecordDecoder::CCODE_OPEN_PARENTHESES);
                    /** @var $value OrientDBRecord */
                    $buffer .= $value->__toString();
                    $buffer .= chr(OrientDBRecordDecoder::CCODE_CLOSE_PARENTHESES);
                    break;

                default:
                    throw new OrientDBException('Can\'t serialize: ' . $valueType);
            }
            $tokens[] = $buffer;
        }
        return $tokens;
    }