LCOV - code coverage report
Current view: top level - libreoffice/workdir/unxlngi6.pro/UnpackedTarball/orcus/include/orcus - css_parser.hpp (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 0 214 0.0 %
Date: 2012-12-17 Functions: 0 33 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*************************************************************************
       2             :  *
       3             :  * Copyright (c) 2011 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_CSS_PARSER_HPP__
      29             : #define __ORCUS_CSS_PARSER_HPP__
      30             : 
      31             : #define ORCUS_DEBUG_CSS 0
      32             : 
      33             : #include <cstdlib>
      34             : #include <cstring>
      35             : #include <exception>
      36             : #include <string>
      37             : #include <cassert>
      38             : #include <sstream>
      39             : 
      40             : #if ORCUS_DEBUG_CSS
      41             : #include <iostream>
      42             : #endif
      43             : 
      44             : namespace orcus {
      45             : 
      46             : class css_parse_error : public std::exception
      47             : {
      48             :     std::string m_msg;
      49             : public:
      50           0 :     css_parse_error(const std::string& msg) : m_msg(msg) {}
      51           0 :     virtual ~css_parse_error() throw() {}
      52           0 :     virtual const char* what() const throw() { return m_msg.c_str(); }
      53             : };
      54             : 
      55             : template<typename _Handler>
      56             : class css_parser
      57             : {
      58             : public:
      59             :     typedef _Handler handler_type;
      60             : 
      61             :     css_parser(const char* p, size_t n, handler_type& hdl);
      62             :     void parse();
      63             : 
      64             : private:
      65             :     // Handlers - at the time a handler is called the current position is
      66             :     // expected to point to the first unprocessed non-blank character, and
      67             :     // each handler must set the current position to the next unprocessed
      68             :     // non-blank character when it finishes.
      69             :     void rule();
      70             :     void at_rule_name();
      71             :     void selector_name();
      72             :     void property_name();
      73             :     void property();
      74             :     void quoted_value();
      75             :     void value();
      76             :     void name_sep();
      77             :     void property_sep();
      78             :     void block();
      79             : 
      80             :     void identifier(const char*& p, size_t& len);
      81             : 
      82             :     void skip_blanks();
      83             :     void skip_blanks_reverse();
      84             :     void shrink_stream();
      85             :     void next();
      86             :     char cur_char() const;
      87             : 
      88           0 :     size_t remaining_size() const { return m_length - m_pos - 1; }
      89           0 :     bool has_char() const { return m_pos < m_length; }
      90             : 
      91           0 :     static bool is_blank(char c)
      92             :     {
      93           0 :         return c == ' ' || c == '\t' || c == '\n';
      94             :     }
      95             : 
      96           0 :     static bool is_alpha(char c)
      97             :     {
      98           0 :         if ('a' <= c && c <= 'z')
      99           0 :             return true;
     100           0 :         if ('A' <= c && c <= 'Z')
     101           0 :             return true;
     102           0 :         return false;
     103             :     }
     104             : 
     105           0 :     static bool is_name_char(char c)
     106             :     {
     107           0 :         switch (c)
     108             :         {
     109             :             case '-':
     110           0 :                 return true;
     111             :         }
     112             : 
     113           0 :         return false;
     114             :     }
     115             : 
     116           0 :     static bool is_numeric(char c)
     117             :     {
     118           0 :         if ('0' <= c && c <= '9')
     119           0 :             return true;
     120           0 :         return false;
     121             :     }
     122             : 
     123             :     handler_type& m_handler;
     124             :     const char* mp_char;
     125             :     size_t m_pos;
     126             :     size_t m_length;
     127             : };
     128             : 
     129             : template<typename _Handler>
     130           0 : css_parser<_Handler>::css_parser(const char* p, size_t n, handler_type& hdl) :
     131           0 :     m_handler(hdl), mp_char(p), m_pos(0), m_length(n) {}
     132             : 
     133             : template<typename _Handler>
     134           0 : void css_parser<_Handler>::parse()
     135             : {
     136           0 :     shrink_stream();
     137             : 
     138             : #if ORCUS_DEBUG_CSS
     139             :     std::cout << "compressed: '";
     140             :     const char* p = mp_char;
     141             :     for (size_t i = m_pos; i < m_length; ++i, ++p)
     142             :         std::cout << *p;
     143             :     std::cout << "'" << std::endl;
     144             : #endif
     145           0 :     m_handler.begin_parse();
     146           0 :     while (has_char())
     147           0 :         rule();
     148           0 :     m_handler.end_parse();
     149           0 : }
     150             : 
     151             : template<typename _Handler>
     152           0 : void css_parser<_Handler>::rule()
     153             : {
     154             :     // <selector name> , ... , <selector name> <block>
     155           0 :     while (has_char())
     156             :     {
     157           0 :         char c = cur_char();
     158           0 :         if (is_alpha(c) || c == '.' || c == '@')
     159             :         {
     160           0 :             selector_name();
     161             :         }
     162           0 :         else if (c == ',')
     163             :         {
     164           0 :             name_sep();
     165             :         }
     166           0 :         else if (c == '{')
     167             :         {
     168           0 :             block();
     169             :         }
     170             :         else
     171             :         {
     172           0 :             std::ostringstream os;
     173           0 :             os << "failed to parse '" << c << "'";
     174           0 :             throw css_parse_error(os.str());
     175             :         }
     176             :     }
     177           0 : }
     178             : 
     179             : template<typename _Handler>
     180           0 : void css_parser<_Handler>::at_rule_name()
     181             : {
     182           0 :     assert(has_char());
     183           0 :     assert(cur_char() == '@');
     184           0 :     next();
     185           0 :     char c = cur_char();
     186           0 :     if (!is_alpha(c))
     187           0 :         throw css_parse_error("first character of an at-rule name must be an alphabet.");
     188             : 
     189             :     const char* p;
     190             :     size_t len;
     191           0 :     identifier(p, len);
     192           0 :     skip_blanks();
     193             : 
     194           0 :     m_handler.at_rule_name(p, len);
     195             : #if ORCUS_DEBUG_CSS
     196             :     std::string foo(p, len);
     197             :     std::cout << "at-rule name: " << foo.c_str() << std::endl;
     198             : #endif
     199           0 : }
     200             : 
     201             : template<typename _Handler>
     202           0 : void css_parser<_Handler>::selector_name()
     203             : {
     204             :     // <element name>
     205             :     // '.' <class name>
     206             :     // <element name> '.' <class name>
     207             :     //
     208             :     // Both element and class names are identifiers.
     209             : 
     210           0 :     assert(has_char());
     211           0 :     char c = cur_char();
     212           0 :     if (c == '@')
     213             :     {
     214             :         // This is the name of an at-rule.
     215           0 :         at_rule_name();
     216           0 :         return;
     217             :     }
     218             : 
     219           0 :     if (!is_alpha(c) && c != '.')
     220           0 :         throw css_parse_error("first character of a name must be an alphabet or a dot.");
     221             : 
     222           0 :     const char* p_elem = NULL;
     223           0 :     const char* p_class = NULL;
     224           0 :     size_t len_elem = 0;
     225           0 :     size_t len_class = 0;
     226           0 :     if (c != '.')
     227           0 :         identifier(p_elem, len_elem);
     228             : 
     229           0 :     if (cur_char() == '.')
     230             :     {
     231           0 :         next();
     232           0 :         identifier(p_class, len_class);
     233             :     }
     234           0 :     skip_blanks();
     235             : 
     236           0 :     m_handler.selector_name(p_elem, len_elem, p_class, len_class);
     237             : #if ORCUS_DEBUG_CSS
     238             :     std::string elem_name(p_elem, len_elem), class_name(p_class, len_class);
     239             :     std::cout << "selector name: (element)'" << elem_name.c_str() << "' (class)'" << class_name.c_str() << "'" << std::endl;
     240             : #endif
     241             : }
     242             : 
     243             : template<typename _Handler>
     244           0 : void css_parser<_Handler>::property_name()
     245             : {
     246             :     // <identifier>
     247             : 
     248           0 :     assert(has_char());
     249           0 :     char c = cur_char();
     250           0 :     if (!is_alpha(c) && c != '.')
     251           0 :         throw css_parse_error("first character of a name must be an alphabet or a dot.");
     252             : 
     253             :     const char* p;
     254             :     size_t len;
     255           0 :     identifier(p, len);
     256           0 :     skip_blanks();
     257             : 
     258           0 :     m_handler.property_name(p, len);
     259             : #if ORCUS_DEBUG_CSS
     260             :     std::string foo(p, len);
     261             :     std::cout << "property name: " << foo.c_str() << std::endl;
     262             : #endif
     263           0 : }
     264             : 
     265             : template<typename _Handler>
     266           0 : void css_parser<_Handler>::property()
     267             : {
     268             :     // <property name> : <value> , ... , <value>
     269             : 
     270           0 :     m_handler.begin_property();
     271           0 :     property_name();
     272           0 :     if (cur_char() != ':')
     273           0 :         throw css_parse_error("':' expected.");
     274           0 :     next();
     275           0 :     skip_blanks();
     276           0 :     while (has_char())
     277             :     {
     278           0 :         value();
     279           0 :         char c = cur_char();
     280           0 :         if (c == ',')
     281             :         {
     282             :             // separated by commas.
     283           0 :             next();
     284           0 :             skip_blanks();
     285             :         }
     286           0 :         else if (c == ';')
     287           0 :             break;
     288             :     }
     289           0 :     skip_blanks();
     290           0 :     m_handler.end_property();
     291           0 : }
     292             : 
     293             : template<typename _Handler>
     294           0 : void css_parser<_Handler>::quoted_value()
     295             : {
     296             :     // Parse until the the end quote is reached.
     297             : 
     298           0 :     assert(cur_char() == '"');
     299           0 :     next();
     300           0 :     const char* p = mp_char;
     301           0 :     size_t len = 1;
     302           0 :     for (next(); has_char(); next())
     303             :     {
     304           0 :         if (cur_char() == '"')
     305             :         {
     306             :             // End quote reached.
     307           0 :             break;
     308             :         }
     309           0 :         ++len;
     310             :     }
     311             : 
     312           0 :     if (cur_char() != '"')
     313           0 :         throw css_parse_error("end quote has never been reached.");
     314             : 
     315           0 :     next();
     316           0 :     skip_blanks();
     317             : 
     318           0 :     m_handler.value(p, len);
     319             : #if ORCUS_DEBUG_CSS
     320             :     std::string foo(p, len);
     321             :     std::cout << "quoted value: " << foo.c_str() << std::endl;
     322             : #endif
     323           0 : }
     324             : 
     325             : template<typename _Handler>
     326           0 : void css_parser<_Handler>::value()
     327             : {
     328           0 :     assert(has_char());
     329           0 :     char c = cur_char();
     330           0 :     if (c == '"')
     331             :     {
     332           0 :         quoted_value();
     333           0 :         return;
     334             :     }
     335             : 
     336           0 :     if (!is_alpha(c) && !is_numeric(c) && c != '-' && c != '+' && c != '.')
     337             :     {
     338           0 :         std::ostringstream os;
     339           0 :         os << "illegal first character of a value '" << c << "'";
     340           0 :         throw css_parse_error(os.str());
     341             :     }
     342             : 
     343           0 :     const char* p = mp_char;
     344           0 :     size_t len = 1;
     345           0 :     for (next(); has_char(); next())
     346             :     {
     347           0 :         c = cur_char();
     348           0 :         if (!is_alpha(c) && !is_name_char(c) && !is_numeric(c) && c != '.')
     349           0 :             break;
     350           0 :         ++len;
     351             :     }
     352           0 :     skip_blanks();
     353             : 
     354           0 :     m_handler.value(p, len);
     355             : #if ORCUS_DEBUG_CSS
     356             :     std::string foo(p, len);
     357             :     std::cout << "value: " << foo.c_str() << std::endl;
     358             : #endif
     359             : }
     360             : 
     361             : template<typename _Handler>
     362           0 : void css_parser<_Handler>::name_sep()
     363             : {
     364           0 :     assert(cur_char() == ',');
     365             : #if ORCUS_DEBUG_CSS
     366             :     std::cout << "," << std::endl;
     367             : #endif
     368           0 :     next();
     369           0 :     skip_blanks();
     370           0 : }
     371             : 
     372             : template<typename _Handler>
     373           0 : void css_parser<_Handler>::property_sep()
     374             : {
     375             : #if ORCUS_DEBUG_CSS
     376             :     std::cout << ";" << std::endl;
     377             : #endif
     378           0 :     next();
     379           0 :     skip_blanks();
     380           0 : }
     381             : 
     382             : template<typename _Handler>
     383           0 : void css_parser<_Handler>::block()
     384             : {
     385             :     // '{' <property> ';' ... ';' <property> ';'(optional) '}'
     386             : 
     387           0 :     assert(cur_char() == '{');
     388             : #if ORCUS_DEBUG_CSS
     389             :     std::cout << "{" << std::endl;
     390             : #endif
     391           0 :     m_handler.begin_block();
     392             : 
     393           0 :     next();
     394           0 :     skip_blanks();
     395             : 
     396             :     // parse properties.
     397           0 :     while (has_char())
     398             :     {
     399           0 :         property();
     400           0 :         if (cur_char() != ';')
     401           0 :             break;
     402           0 :         property_sep();
     403           0 :         if (cur_char() == '}')
     404             :             // ';' after the last property.  This is optional but allowed.
     405           0 :             break;
     406             :     }
     407             : 
     408           0 :     if (cur_char() != '}')
     409           0 :         throw css_parse_error("} expected.");
     410             : 
     411           0 :     m_handler.end_block();
     412             : 
     413           0 :     next();
     414           0 :     skip_blanks();
     415             : 
     416             : #if ORCUS_DEBUG_CSS
     417             :     std::cout << "}" << std::endl;
     418             : #endif
     419           0 : }
     420             : 
     421             : template<typename _Handler>
     422           0 : void css_parser<_Handler>::identifier(const char*& p, size_t& len)
     423             : {
     424           0 :     p = mp_char;
     425           0 :     len = 1;
     426           0 :     for (next(); has_char(); next())
     427             :     {
     428           0 :         char c = cur_char();
     429           0 :         if (!is_alpha(c) && !is_name_char(c) && !is_numeric(c))
     430           0 :             break;
     431           0 :         ++len;
     432             :     }
     433           0 : }
     434             : 
     435             : template<typename _Handler>
     436           0 : void css_parser<_Handler>::skip_blanks()
     437             : {
     438           0 :     for (; has_char(); next())
     439             :     {
     440           0 :         if (!is_blank(*mp_char))
     441           0 :             break;
     442             :     }
     443           0 : }
     444             : 
     445             : template<typename _Handler>
     446           0 : void css_parser<_Handler>::skip_blanks_reverse()
     447             : {
     448           0 :     const char* p = mp_char + remaining_size();
     449           0 :     for (; p != mp_char; --p, --m_length)
     450             :     {
     451           0 :         if (!is_blank(*p))
     452           0 :             break;
     453             :     }
     454           0 : }
     455             : 
     456             : template<typename _Handler>
     457           0 : void css_parser<_Handler>::shrink_stream()
     458             : {
     459             :     // Skip any leading blanks.
     460           0 :     skip_blanks();
     461             : 
     462           0 :     if (!remaining_size())
     463           0 :         return;
     464             : 
     465             :     // Skip any trailing blanks.
     466           0 :     skip_blanks_reverse();
     467             : 
     468             :     // Skip leading <!-- if present.
     469             : 
     470           0 :     const char* com_open = "<!--";
     471           0 :     size_t com_open_len = std::strlen(com_open);
     472           0 :     if (remaining_size() < com_open_len)
     473             :         // Not enough stream left.  Bail out.
     474           0 :         return;
     475             : 
     476           0 :     const char* p = mp_char;
     477           0 :     for (size_t i = 0; i < com_open_len; ++i, ++p)
     478             :     {
     479           0 :         if (*p != com_open[i])
     480           0 :             return;
     481           0 :         next();
     482             :     }
     483           0 :     mp_char = p;
     484             : 
     485             :     // Skip leading blanks once again.
     486           0 :     skip_blanks();
     487             : 
     488             :     // Skip trailing --> if present.
     489           0 :     const char* com_close = "-->";
     490           0 :     size_t com_close_len = std::strlen(com_close);
     491           0 :     size_t n = remaining_size();
     492           0 :     if (n < com_close_len)
     493             :         // Not enough stream left.  Bail out.
     494           0 :         return;
     495             : 
     496           0 :     p = mp_char + n; // move to the last char.
     497           0 :     for (size_t i = com_close_len; i > 0; --i, --p)
     498             :     {
     499           0 :         if (*p != com_close[i-1])
     500           0 :             return;
     501             :     }
     502           0 :     m_length -= com_close_len;
     503             : 
     504           0 :     skip_blanks_reverse();
     505             : }
     506             : 
     507             : template<typename _Handler>
     508           0 : void css_parser<_Handler>::next()
     509             : {
     510           0 :     ++m_pos;
     511           0 :     ++mp_char;
     512           0 : }
     513             : 
     514             : template<typename _Handler>
     515           0 : char css_parser<_Handler>::cur_char() const
     516             : {
     517           0 :     return *mp_char;
     518             : }
     519             : 
     520             : }
     521             : 
     522             : #endif

Generated by: LCOV version 1.10