text: /*
  ** Copyright 2016 Centreon
  **
  ** Licensed under the Apache License, Version 2.0 (the "License");
  ** you may not use this file except in compliance with the License.
  ** You may obtain a copy of the License at
  **
  **     http://www.apache.org/licenses/LICENSE-2.0
  **
  ** Unless required by applicable law or agreed to in writing, software
  ** distributed under the License is distributed on an "AS IS" BASIS,
  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ** See the License for the specific language governing permissions and
  ** limitations under the License.
  **
  ** For more information : contact@centreon.com
  */
  
  #include "com/centreon/broker/bam/exp_tokenizer.hh"
  
  #include <cctype>
  #include "com/centreon/exceptions/msg_fmt.hh"
  
  using namespace com::centreon::exceptions;
  using namespace com::centreon::broker;
  using namespace com::centreon::broker::bam;
  
  /**
   *  Constructor.
   */
  exp_tokenizer::exp_tokenizer(std::string const& text)
      : _current(0), _next(0), _text(text) {
    _size = _text.size();
  }
  
  /**
   *  Copy constructor.
   *
   *  @param[in] other  Object to copy.
   */
  exp_tokenizer::exp_tokenizer(exp_tokenizer const& other) {
    _internal_copy(other);
  }
  
  /**
   *  Destructor.
   */
  exp_tokenizer::~exp_tokenizer() {}
  
  /**
   *  Assignment operator.
   *
   *  @param[in] other  Object to copy.
   *
   *  @return This object.
   */
  exp_tokenizer& exp_tokenizer::operator=(exp_tokenizer const& other) {
    if (this != &other)
      _internal_copy(other);
    return (*this);
  }
  
  /**
   *  Get next token.
   *
   *  @return Next token. Empty if end of expression is reached.
   */
  std::string exp_tokenizer::next() {
    // If queue is not empty, tokens are already available.
    std::string retval;
    if (!_queue.empty()) {
      retval = _queue.front();
      _queue.pop();
    }
    // Extract new token.
    else {
      // Move to next token.
      _current = _next;
      _skip_ws();
      _next = _current;
  
      // Check if there are more tokens to read.
      if (_current < _size) {
        // Special char. Most of them are single-charaters token.
        if (_is_special_char()) {
          if ((_current + 1 < _size)
              // Double character exceptions: ==, !=, >=, <=.
              && ((((_text[_current] == '=') || (_text[_current] == '!') ||
                    (_text[_current] == '<') || (_text[_current] == '>')) &&
                   (_text[_current + 1] == '='))
                  // Double character exceptions: ||, &&.
                  || (((_text[_current] == '|') || (_text[_current] == '&')) &&
                      (_text[_current] == _text[_current + 1])))) {
            retval.push_back(_text[_current]);
            retval.push_back(_text[_current + 1]);
            _next = _current + 2;
          } else {
            retval.push_back(_text[_current]);
            _next = _current + 1;
          }
        }
        // This seems like a classical token, extract it.
        else {
          retval = _extract_token();
        }
      }
    }
    return (retval);
  }
  
  /**
   *  Extract next token from text.
   *
   *  @return Next token.
   */
  std::string exp_tokenizer::_extract_token() {
    // Return value.
    std::string retval;
  
    /*
    ** Well, the whole brace parsing is inherited from the old
    ** "{Host Service} {IS} {OK}"-style syntax which SUCKS. The following
    ** code should not exist at all or maybe should be placed within the
    ** parser. However the parser being already complex enough, this
    ** seemed like a good place.
    */
  
    // Brace-delimited token, this is special.
    if (_current < _size && _text[_current] == '{') {
      // Extract token.
      ++_current;
      retval = _extract_until(&exp_tokenizer::_is_space_or_right_brace);
  
      // If it's an operator or a constant, leave it as it is.
      // Otherwise this express the STATUS of a host/service.
      if (retval != "IS" && retval != "NOT" && retval != "AND" &&
          retval != "XOR" && retval != "OR" && retval != "OK" &&
          retval != "WARNING" && retval != "CRITICAL" && retval != "UNKNOWN" &&
          retval != "UP" && retval != "DOWN" && retval != "UNREACHABLE") {
        // Host name is retrieved first.
        _queue.push("(");
        _queue.push(retval);
        _current = _next;
        _skip_ws();
        _next = _current;
  
        // Extract service if possible.
        if ((_current < _size) && (_text[_current] != '}')) {
          retval = _extract_until(&exp_tokenizer::_is_right_brace);
          _queue.push(",");
          _queue.push(retval);
          retval = "SERVICESTATUS";
        }
        // No service, wish to retrieve host status.
        else {
          retval = "HOSTSTATUS";
        }
        _queue.push(")");
      }
  
      // Assert that ending brace was found.
      if ((_next < _size) && (_text[_next] == '}')) {
        ++_next;
      } else
        throw msg_fmt(
            "opening brace at position {}"
            " has no ending brace ",
            _current);
    }
    // Extract classical token.
    else {
      retval = _extract_until(&exp_tokenizer::_is_delimiter);
    }
  
    return (retval);
  }
  
  /**
   *  Extract a token until a predicate.
   *
   *  @param[in] predicate  Predicate method.
   *
   *  @return Extracted token.
   */
  std::string exp_tokenizer::_extract_until(bool (exp_tokenizer::*predicate)()) {
    std::string retval;
    _next = _current;
    while ((_next < _size) && !(this->*predicate)()) {
      switch (_text[_next]) {
        case '\'':
        case '"': {
          char quote(_text[_next]);
          bool process_metachars(quote == '"');
          bool quote_matched(false);
          while ((++_next < _size)) {
            if (_text[_next] == quote) {
              quote_matched = true;
              break;
            }
            if (process_metachars && (_text[_next] == '\\')) {
              ++_next;
              if (_next < _size) {
                retval.push_back(_text[_next]);
              }
            } else {
              retval.push_back(_text[_next]);
            }
          }
          if (!quote_matched)
            throw msg_fmt("unterminated {} quote in the following expression: {}",
                          (process_metachars ? "double" : "single"), _text);
        } break;
        case '\\':
          if (++_next < _size)
            retval.push_back(_text[_next]);
          break;
        default:
          retval.push_back(_text[_next]);
      }
      ++_next;
    }
  
    return (retval);
  }
  
  /**
   *  Copy internal data members.
   *
   *  @param[in] other  Object to copy.
   */
  void exp_tokenizer::_internal_copy(exp_tokenizer const& other) {
    _current = other._current;
    _next = other._next;
    _queue = other._queue;
    _size = other._size;
    _text = other._text;
    return;
  }
  
  /**
   *  Check if the next character is a right brace.
   *
   *  @return True if the next character is a right brace.
   */
  bool exp_tokenizer::_is_right_brace() {
    return (_text[_next] == '}');
  }
  
  /**
   *  Check if the next character is a right brace or a space.
   *
   *  @return True if the next character is a right brace or a space.
   */
  bool exp_tokenizer::_is_space_or_right_brace() {
    return (isspace(_text[_next]) || _is_right_brace());
  }
  
  /**
   *  Check if the next character is a special character for the lexer.
   *
   *  @return True if the next character is special.
   */
  bool exp_tokenizer::_is_special_char() {
    switch (_text[_next]) {
      case '(':
      case ')':
      case ',':
      case '+':
      case '-':
      case '*':
      case '/':
      case '%':
      case '=':
      case '!':
      case '>':
      case '<':
      case '|':
      case '&':
        return (true);
    }
    return (false);
  }
  
  /**
   *  Check if the next character is a delimiter.
   *
   *  @return True if the next character is a delimiter.
   */
  bool exp_tokenizer::_is_delimiter() {
    return (isspace(_text[_next]) || _is_special_char());
  }
  
  /**
   *  Skip whitespaces.
   */
  void exp_tokenizer::_skip_ws() {
    while (isspace(_text[_current])) {
      ++_current;
    }
    return;
  }
  