DefaultKey.py 5.81 KB
Newer Older
Ivan Tyagov's avatar
Ivan Tyagov committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
##############################################################################
#
# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
#                     Ivan Tyagov <ivan@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################
Jean-Paul Smets's avatar
Jean-Paul Smets committed
28

29
from SearchKey import SearchKey
Ivan Tyagov's avatar
Ivan Tyagov committed
30 31
from pprint import pprint

32
class DefaultKey(SearchKey):
Ivan Tyagov's avatar
Ivan Tyagov committed
33 34 35 36
  """ DefaultKey key is an ERP5 portal_catalog search key which is used to render
      SQL expression that will try to exactly one value.
      It supports following special operator ['=', '%', '>' , '>=', '<', '<='] in
      addition to main logical operators like ['OR', 'or', 'AND', 'and'].
37

Jean-Paul Smets's avatar
Jean-Paul Smets committed
38
      Examples for title column:
Ivan Tyagov's avatar
Ivan Tyagov committed
39 40 41 42 43 44 45
        * 'foo or bar'  --> "title = 'foo' OR title = 'bar'"
        * 'foo or =bar'  --> "title = 'foo' OR title = 'bar'"
        * '%foo% or bar' --> "title = '%foo%' OR title = 'bar'"
        * 'Organisation Module' -->  "title = 'Organisation Module'"
        * '"Organisation Module"' --> "title = 'Organisation Module'"
        * '="Organisation Module"' --> "title = 'Organisation Module'"
  """
46

Ivan Tyagov's avatar
Ivan Tyagov committed
47 48
  # default type of sub Queries to be generated out fo a search string
  default_key_type = 'default'
49

Ivan Tyagov's avatar
Ivan Tyagov committed
50
  tokens =  ('OR', 'AND', 'NOT', 'WORDSET', 'WORD',
Jean-Paul Smets's avatar
Jean-Paul Smets committed
51
             'GREATERTHAN', 'GREATERTHANEQUAL',
Ivan Tyagov's avatar
Ivan Tyagov committed
52
             'LESSTHAN', 'LESSTHANEQUAL')
53

Jean-Paul Smets's avatar
Jean-Paul Smets committed
54
  sub_operators = ('GREATERTHAN', 'GREATERTHANEQUAL',
Ivan Tyagov's avatar
Ivan Tyagov committed
55
                    'LESSTHAN', 'LESSTHANEQUAL', 'NOT')
56

Ivan Tyagov's avatar
Ivan Tyagov committed
57 58 59 60 61 62 63 64 65 66 67 68 69 70

  # Note: Order of placing rules (t_WORD for example) is very important
  def t_OR(self, t):
    r'(\s+OR\s+|\s+or\s+)'
    # operator must have leading and trailing ONLY one white space character
    # otherwise it's treated as a WORD
    t.value = 'OR'
    return t

  def t_AND(self, t):
    r'(\s+AND\s+|\s+and\s+)'
    # operator must have leading and trailing ONLY one white space character
    # otherwise it's treated as a WORD
    t.value = 'AND'
Jean-Paul Smets's avatar
Jean-Paul Smets committed
71
    return t
72

Ivan Tyagov's avatar
Ivan Tyagov committed
73 74 75 76
  def t_NOT(self, t):
    r'(\s+NOT\s+|\s+not\s+|!=)'
    # operator must have leading and trailing ONLY one white space character
    # otherwise it's treated as a WORD
Jean-Paul Smets's avatar
Jean-Paul Smets committed
77 78
    t.value = '!='
    return t
79

Jean-Paul Smets's avatar
Jean-Paul Smets committed
80 81
  t_GREATERTHANEQUAL = r'>='
  t_LESSTHANEQUAL = r'<='
Ivan Tyagov's avatar
Ivan Tyagov committed
82
  t_GREATERTHAN = r'>'
Jean-Paul Smets's avatar
Jean-Paul Smets committed
83
  t_LESSTHAN = r'<'
Ivan Tyagov's avatar
Ivan Tyagov committed
84 85

  def t_WORD(self, t):
86
    r'[\x7F-\xFF\w\d\/\-~!@#$%^&*()_+\n][\x7F-\xFF\w\d\/\-~!@#$%^&*()_+\n]*'
Ivan Tyagov's avatar
Ivan Tyagov committed
87 88 89 90 91
    #r'[\x7F-\xFF\w\d\/%][\x7F-\xFF\w\d\/%]*'
    # WORD may contain arbitrary letters and numbers without white space
    # WORD may contain '%' but not at the beginning or end (otherwise it's KEYWORD)
    value = t.value.strip()
    t.value = "%s" %value
92 93
    return t

Ivan Tyagov's avatar
Ivan Tyagov committed
94 95 96 97 98 99 100
  def t_WORDSET(self, t):
    r'"[\x7F-\xFF\w\d\s\/~!@#$%^&*()_+][\x7F-\xFF\w\d\s\/~!@#$%^&*()_+]*"'
    #r'"[\x7F-\xFF\w\d\s/%][\x7F-\xFF\w\d\s/%]*"'
    # WORDSET is a combination of WORDs separated by white space
    # and starting/ending with "
    value = t.value.replace('"', '').strip()
    t.value = "%s" %value
Jean-Paul Smets's avatar
Jean-Paul Smets committed
101
    return t
102

Ivan Tyagov's avatar
Ivan Tyagov committed
103 104 105 106 107 108
  def quoteSQLString(self, value, format):
    """ Return a quoted string of the value. """
    if isinstance(value, (int, long,)):
      return str(value)
    return "'%s'" %value

109

Ivan Tyagov's avatar
Ivan Tyagov committed
110 111 112 113 114 115
##  def buildSQLExpressionFromSearchString(self, key, value, format, mode, range_value, stat__):
##    """ Tokenize/analyze passed string value and generate SQL query expressions. """
##    where_expressions = []
##    select_expressions = []
##    tokens = self.tokenize(value)
##    operators_mapping_list = self.groupByOperator(tokens)
116
##
Ivan Tyagov's avatar
Ivan Tyagov committed
117 118 119 120 121 122 123 124
##    # find if any logical operator exists
##    tokens_values = []
##    logical_operator_found = 0
##    for token in tokens:
##      if token.type not in ('WORDSET', 'WORD',):
##        logical_operator_found = 1
##        break
##      tokens_values.append(token.value.replace("'", ""))
125
##
Ivan Tyagov's avatar
Ivan Tyagov committed
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
##    # build expressions
##    if not logical_operator_found:
##      # no logical operator found so we assume that we search for a combination of words
##      where_expressions.append("%s = '%s'" %(key, ' '.join(tokens_values)))
##    else:
##      # in the search string we have explicitly defined an operator
##      for item in operators_mapping_list:
##        row_tokens_values = []
##        tokens = item['tokens']
##        operator = item['operator']
##        operator_value = None
##        if operator is not None:
##          # operator is standalone expression
##          operator_value = operator.value
##          where_expressions.append('%s' %operator_value)
##        if len(tokens):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
142
##          # no it's not a stand alone expression,
Ivan Tyagov's avatar
Ivan Tyagov committed
143 144 145 146
##          # determine it from list of tokens
##          operator_value, sub_tokens = self.getOperatorForTokenList(tokens)
##          row_tokens_values = [x.value for x in sub_tokens]
##          where_expressions.append("%s %s '%s'" %(key, operator_value, ' '.join(row_tokens_values)))
Jean-Paul Smets's avatar
Jean-Paul Smets committed
147
##    return where_expressions, select_expressions