LCOV - code coverage report
Current view: top level - sc/inc/orcus - csv_parser.hpp (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 126 135 93.3 %
Date: 2012-08-25 Functions: 34 44 77.3 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 66 140 47.1 %

           Branch data     Line data    Source code
       1                 :            : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 :            : /*************************************************************************
       3                 :            :  *
       4                 :            :  * Copyright (c) 2011 Kohei Yoshida
       5                 :            :  *
       6                 :            :  * Permission is hereby granted, free of charge, to any person
       7                 :            :  * obtaining a copy of this software and associated documentation
       8                 :            :  * files (the "Software"), to deal in the Software without
       9                 :            :  * restriction, including without limitation the rights to use,
      10                 :            :  * copy, modify, merge, publish, distribute, sublicense, and/or sell
      11                 :            :  * copies of the Software, and to permit persons to whom the
      12                 :            :  * Software is furnished to do so, subject to the following
      13                 :            :  * conditions:
      14                 :            :  *
      15                 :            :  * The above copyright notice and this permission notice shall be
      16                 :            :  * included in all copies or substantial portions of the Software.
      17                 :            :  *
      18                 :            :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      19                 :            :  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
      20                 :            :  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
      21                 :            :  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
      22                 :            :  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
      23                 :            :  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      24                 :            :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
      25                 :            :  * OTHER DEALINGS IN THE SOFTWARE.
      26                 :            :  *
      27                 :            :  ************************************************************************/
      28                 :            : 
      29                 :            : #ifndef __ORCUS_CSV_PARSER_HPP__
      30                 :            : #define __ORCUS_CSV_PARSER_HPP__
      31                 :            : 
      32                 :            : #define ORCUS_DEBUG_CSV 0
      33                 :            : 
      34                 :            : #include <cstdlib>
      35                 :            : #include <cstring>
      36                 :            : #include <exception>
      37                 :            : #include <string>
      38                 :            : #include <cassert>
      39                 :            : #include <sstream>
      40                 :            : 
      41                 :            : #if ORCUS_DEBUG_CSV
      42                 :            : #include <iostream>
      43                 :            : using std::cout;
      44                 :            : using std::endl;
      45                 :            : #endif
      46                 :            : 
      47                 :            : namespace orcus {
      48                 :            : 
      49                 :         87 : struct csv_parser_config
      50                 :            : {
      51                 :            :     std::string delimiters;
      52                 :            :     char text_qualifier;
      53                 :            :     bool trim_cell_value:1;
      54                 :            : 
      55                 :         87 :     csv_parser_config() :
      56                 :         87 :         trim_cell_value(true) {}
      57                 :            : };
      58                 :            : 
      59                 :            : class csv_parse_error : public std::exception
      60                 :            : {
      61                 :            :     std::string m_msg;
      62                 :            : public:
      63         [ #  # ]:          0 :     csv_parse_error(const std::string& msg) : m_msg(msg) {}
      64         [ #  # ]:          0 :     virtual ~csv_parse_error() throw() {}
      65                 :          0 :     virtual const char* what() const throw() { return m_msg.c_str(); }
      66                 :            : };
      67                 :            : 
      68                 :            : template<typename _Handler>
      69                 :         87 : class csv_parser
      70                 :            : {
      71                 :            : public:
      72                 :            :     typedef _Handler handler_type;
      73                 :            : 
      74                 :            :     csv_parser(const char* p, size_t n, handler_type& hdl, const csv_parser_config& config);
      75                 :            :     void parse();
      76                 :            : 
      77                 :            : private:
      78                 :      34059 :     bool has_char() const { return m_pos < m_length; }
      79                 :        609 :     bool has_next() const { return m_pos + 1 < m_length; }
      80                 :            :     void next();
      81                 :            :     char cur_char() const;
      82                 :            :     char next_char() const;
      83                 :            : 
      84                 :            :     bool is_delim(char c) const;
      85                 :            :     bool is_text_qualifier(char c) const;
      86                 :            : 
      87                 :            :     // handlers
      88                 :            :     void row();
      89                 :            :     void cell();
      90                 :            :     void quoted_cell();
      91                 :            : 
      92                 :            :     void parse_cell_with_quote(const char* p0, size_t len0);
      93                 :            :     void skip_blanks();
      94                 :            : 
      95                 :            :     void init_cell_buf();
      96                 :            :     void append_to_cell_buf(const char* p, size_t len);
      97                 :            : 
      98                 :            :     /**
      99                 :            :      * Push cell value to the handler.
     100                 :            :      */
     101                 :            :     void push_cell_value(const char* p, size_t n);
     102                 :            : 
     103                 :        333 :     static bool is_blank(char c)
     104                 :            :     {
     105 [ +  - ][ -  + ]:        333 :         return c == ' ' || c == '\t';
         [ +  - ][ -  + ]
     106                 :            :     }
     107                 :            : 
     108                 :            : private:
     109                 :            :     handler_type& m_handler;
     110                 :            :     const csv_parser_config& m_config;
     111                 :            :     std::string m_cell_buf;
     112                 :            :     const char* mp_char;
     113                 :            :     size_t m_pos;
     114                 :            :     size_t m_length;
     115                 :            :     size_t m_cell_buf_size;
     116                 :            : };
     117                 :            : 
     118                 :            : template<typename _Handler>
     119                 :         87 : csv_parser<_Handler>::csv_parser(const char* p, size_t n, handler_type& hdl, const csv_parser_config& config) :
     120                 :         87 :     m_handler(hdl), m_config(config), mp_char(p), m_pos(0), m_length(n) {}
     121                 :            : 
     122                 :            : template<typename _Handler>
     123                 :         87 : void csv_parser<_Handler>::parse()
     124                 :            : {
     125                 :            : #if ORCUS_DEBUG_CSV
     126                 :            :     const char* p = mp_char;
     127                 :            :     for (size_t i = m_pos; i < m_length; ++i, ++p)
     128                 :            :         std::cout << *p;
     129                 :            :     std::cout << std::endl;
     130                 :            : #endif
     131                 :            : 
     132                 :         87 :     m_handler.begin_parse();
     133 [ +  + ][ +  + ]:        942 :     while (has_char())
     134                 :        855 :         row();
     135                 :         87 :     m_handler.end_parse();
     136                 :         87 : }
     137                 :            : 
     138                 :            : template<typename _Handler>
     139                 :      33174 : void csv_parser<_Handler>::next()
     140                 :            : {
     141                 :      33174 :     ++m_pos;
     142                 :      33174 :     ++mp_char;
     143                 :      33174 : }
     144                 :            : 
     145                 :            : template<typename _Handler>
     146                 :      51645 : char csv_parser<_Handler>::cur_char() const
     147                 :            : {
     148                 :      51645 :     return *mp_char;
     149                 :            : }
     150                 :            : 
     151                 :            : template<typename _Handler>
     152                 :        609 : char csv_parser<_Handler>::next_char() const
     153                 :            : {
     154                 :        609 :     return *(mp_char+1);
     155                 :            : }
     156                 :            : 
     157                 :            : template<typename _Handler>
     158                 :      21048 : bool csv_parser<_Handler>::is_delim(char c) const
     159                 :            : {
     160                 :      21048 :     return m_config.delimiters.find(c) != std::string::npos;
     161                 :            : }
     162                 :            : 
     163                 :            : template<typename _Handler>
     164                 :      20577 : bool csv_parser<_Handler>::is_text_qualifier(char c) const
     165                 :            : {
     166                 :      20577 :     return m_config.text_qualifier == c;
     167                 :            : }
     168                 :            : 
     169                 :            : template<typename _Handler>
     170                 :        855 : void csv_parser<_Handler>::row()
     171                 :            : {
     172                 :        855 :     m_handler.begin_row();
     173                 :       8685 :     while (true)
     174                 :            :     {
     175 [ -  + ][ +  + ]:       9540 :         if (is_text_qualifier(cur_char()))
     176                 :        267 :             quoted_cell();
     177                 :            :         else
     178                 :       9273 :             cell();
     179                 :            : 
     180 [ -  + ][ -  + ]:       9540 :         if (!has_char())
     181                 :            :         {
     182                 :          0 :             m_handler.end_row();
     183                 :        855 :             return;
     184                 :            :         }
     185                 :            : 
     186                 :       9540 :         char c = cur_char();
     187   [ +  +  +  + ]:       9540 :         if (c == '\n')
     188                 :            :         {
     189                 :        855 :             next();
     190                 :            : #if ORCUS_DEBUG_CSV
     191                 :            :             cout << "(LF)" << endl;
     192                 :            : #endif
     193                 :        855 :             m_handler.end_row();
     194                 :        855 :             return;
     195                 :            :         }
     196                 :            : 
     197                 :            :         assert(is_delim(c));
     198                 :       8685 :         next();
     199   [ +  -  -  + ]:       8685 :         if(m_config.trim_cell_value)
     200                 :         18 :             skip_blanks();
     201                 :            :     }
     202                 :            : }
     203                 :            : 
     204                 :            : template<typename _Handler>
     205                 :       9273 : void csv_parser<_Handler>::cell()
     206                 :            : {
     207                 :       9273 :     const char* p = mp_char;
     208                 :       9273 :     size_t len = 0;
     209                 :       9273 :     char c = cur_char();
     210 [ +  + ][ +  + ]:      21870 :     while (c != '\n' && !is_delim(c))
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
     211                 :            :     {
     212                 :      12597 :         ++len;
     213                 :      12597 :         next();
     214   [ -  +  -  + ]:      12597 :         if (!has_char())
     215                 :          0 :             break;
     216                 :      12597 :         c = cur_char();
     217                 :            :     }
     218                 :            : 
     219 [ -  + ][ +  + ]:       9273 :     if (!len)
     220                 :       7146 :         p = NULL;
     221                 :            : 
     222                 :       9273 :     push_cell_value(p, len);
     223                 :       9273 : }
     224                 :            : 
     225                 :            : template<typename _Handler>
     226                 :        267 : void csv_parser<_Handler>::quoted_cell()
     227                 :            : {
     228                 :            : #if ORCUS_DEBUG_CSV
     229                 :            :     using namespace std;
     230                 :            :     cout << "--- quoted cell" << endl;
     231                 :            : #endif
     232                 :        267 :     char c = cur_char();
     233                 :            :     assert(is_text_qualifier(c));
     234                 :        267 :     next(); // Skip the opening quote.
     235   [ #  #  -  + ]:        267 :     if (!has_char())
     236                 :          0 :         return;
     237                 :            : 
     238                 :        267 :     const char* p0 = mp_char;
     239                 :        267 :     size_t len = 1;
     240 [ #  # ][ +  - ]:       3093 :     for (; has_char(); next(), ++len)
     241                 :            :     {
     242                 :       3093 :         c = cur_char();
     243                 :            : #if ORCUS_DEBUG_CSV
     244                 :            :         cout << "'" << c << "'" << endl;
     245                 :            : #endif
     246   [ #  #  +  + ]:       3093 :         if (!is_text_qualifier(c))
     247                 :       2826 :             continue;
     248                 :            : 
     249                 :            :         // current char is a quote. Check if the next char is also a text
     250                 :            :         // qualifier.
     251                 :            : 
     252 [ #  # ][ #  # ]:        267 :         if (has_next() && is_text_qualifier(next_char()))
         [ #  # ][ +  - ]
         [ +  + ][ +  + ]
     253                 :            :         {
     254                 :        156 :             next();
     255                 :        156 :             parse_cell_with_quote(p0, len);
     256                 :        156 :             return;
     257                 :            :         }
     258                 :            : 
     259                 :            :         // Closing quote.
     260                 :        111 :         m_handler.cell(p0, len-1);
     261                 :        111 :         next();
     262                 :        111 :         skip_blanks();
     263                 :        111 :         return;
     264                 :            :     }
     265                 :            : 
     266                 :            :     // Stream ended prematurely.  Handle it gracefully.
     267                 :          0 :     m_handler.cell(p0, len);
     268                 :          0 :     next();
     269                 :        267 :     skip_blanks();
     270                 :            : }
     271                 :            : 
     272                 :            : template<typename _Handler>
     273                 :        156 : void csv_parser<_Handler>::parse_cell_with_quote(const char* p0, size_t len0)
     274                 :            : {
     275                 :            : #if ORCUS_DEBUG_CSV
     276                 :            :     using namespace std;
     277                 :            :     cout << "--- parse cell with quote" << endl;
     278                 :            : #endif
     279                 :            :     assert(is_text_qualifier(cur_char()));
     280                 :            : 
     281                 :            :     // Push the preceding chars to the temp buffer.
     282                 :        156 :     init_cell_buf();
     283                 :        156 :     append_to_cell_buf(p0, len0);
     284                 :            : 
     285                 :            :     // Parse the rest, until the closing quote.
     286                 :        156 :     next();
     287                 :        156 :     const char* p_cur = mp_char;
     288                 :        156 :     size_t cur_len = 0;
     289 [ #  # ][ +  - ]:       7335 :     for (; has_char(); next(), ++cur_len)
     290                 :            :     {
     291                 :       7335 :         char c = cur_char();
     292                 :            : #if ORCUS_DEBUG_CSV
     293                 :            :         cout << "'" << c << "'" << endl;
     294                 :            : #endif
     295   [ #  #  +  + ]:       7335 :         if (!is_text_qualifier(c))
     296                 :       6993 :             continue;
     297                 :            : 
     298 [ #  # ][ #  # ]:        342 :         if (has_next() && is_text_qualifier(next_char()))
         [ #  # ][ +  - ]
         [ +  + ][ +  + ]
     299                 :            :         {
     300                 :            :             // double quotation.  Copy the current segment to the cell buffer.
     301                 :        186 :             append_to_cell_buf(p_cur, cur_len);
     302                 :            : 
     303                 :        186 :             next(); // to the 2nd quote.
     304                 :        186 :             p_cur = mp_char;
     305                 :        186 :             cur_len = 0;
     306                 :        186 :             continue;
     307                 :            :         }
     308                 :            : 
     309                 :            :         // closing quote.  Flush the current segment to the cell
     310                 :            :         // buffer, push the value to the handler, and exit normally.
     311                 :        156 :         append_to_cell_buf(p_cur, cur_len);
     312                 :            : 
     313                 :        156 :         m_handler.cell(&m_cell_buf[0], m_cell_buf_size);
     314                 :        156 :         next();
     315                 :        156 :         skip_blanks();
     316                 :        156 :         return;
     317                 :            :     }
     318                 :            : 
     319                 :            :     // Stream ended prematurely.
     320 [ #  # ][ #  # ]:          0 :     throw csv_parse_error("stream ended prematurely while parsing quoted cell.");
         [ #  # ][ #  # ]
     321                 :            : }
     322                 :            : 
     323                 :            : template<typename _Handler>
     324                 :        285 : void csv_parser<_Handler>::skip_blanks()
     325                 :            : {
     326 [ +  - ][ +  - ]:        285 :     for (; has_char(); next())
     327                 :            :     {
     328 [ +  - ][ +  - ]:        285 :         if (!is_blank(*mp_char))
     329                 :        285 :             break;
     330                 :            :     }
     331                 :        285 : }
     332                 :            : 
     333                 :            : template<typename _Handler>
     334                 :        156 : void csv_parser<_Handler>::init_cell_buf()
     335                 :            : {
     336                 :        156 :     m_cell_buf_size = 0;
     337                 :        156 : }
     338                 :            : 
     339                 :            : template<typename _Handler>
     340                 :        498 : void csv_parser<_Handler>::append_to_cell_buf(const char* p, size_t len)
     341                 :            : {
     342                 :        498 :     size_t size_needed = m_cell_buf_size + len;
     343 [ #  # ][ +  + ]:        498 :     if (m_cell_buf.size() < size_needed)
     344                 :         57 :         m_cell_buf.resize(size_needed);
     345                 :            : 
     346                 :        498 :     char* p_dest = &m_cell_buf[m_cell_buf_size];
     347                 :        498 :     std::strncpy(p_dest, p, len);
     348                 :        498 :     m_cell_buf_size += len;
     349                 :        498 : }
     350                 :            : 
     351                 :            : template<typename _Handler>
     352                 :       9273 : void csv_parser<_Handler>::push_cell_value(const char* p, size_t n)
     353                 :            : {
     354                 :       9273 :     size_t len = n;
     355                 :            : 
     356 [ +  - ][ -  + ]:       9273 :     if (m_config.trim_cell_value)
     357                 :            :     {
     358                 :            :         // Trim any leading blanks.
     359 [ +  - ][ #  # ]:         24 :         for (size_t i = 0; i < n; ++i, --len, ++p)
     360                 :            :         {
     361 [ +  - ][ #  # ]:         24 :             if (!is_blank(*p))
     362                 :         24 :                 break;
     363                 :            :         }
     364                 :            : 
     365                 :            :         // Trim any trailing blanks.
     366 [ +  - ][ #  # ]:         24 :         if (len)
     367                 :            :         {
     368                 :         24 :             const char* p_end = p + (len-1);
     369 [ +  - ][ #  # ]:         48 :             for (; p != p_end; --p_end, --len)
     370                 :            :             {
     371 [ +  - ][ #  # ]:         24 :                 if (!is_blank(*p_end))
     372                 :         24 :                     break;
     373                 :            :             }
     374                 :            :         }
     375                 :            :     }
     376                 :            : 
     377                 :       9273 :     m_handler.cell(p, len);
     378                 :            : #if ORCUS_DEBUG_CSV
     379                 :            :     cout << "(cell:'" << std::string(p, len) << "')" << endl;
     380                 :            : #endif
     381                 :       9273 : }
     382                 :            : 
     383                 :            : }
     384                 :            : 
     385                 :            : #endif
     386                 :            : 
     387                 :            : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10