LCOV - code coverage report
Current view: top level - libreoffice/workdir/unxlngi6.pro/UnpackedTarball/orcus/include/orcus - csv_parser.hpp (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 104 127 81.9 %
Date: 2012-12-17 Functions: 30 57 52.6 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10