Package cssutils :: Package css :: Module selector |
[hide private]
[frames] | no frames]

# Source Code for Module cssutils.css.selector

      1  """Selector is a single Selector of a CSSStyleRule SelectorList.
      2
      3  Partly implements
      4      http://www.w3.org/TR/css3-selectors/
      5
      6  TODO
      7      - .contains(selector)
      8      - .isSubselector(selector)
      9  """
     10  __all__ = ['Selector']
     11  __docformat__ = 'restructuredtext'
     12  __version__ = '$Id: selector.py 1429 2008-08-11 19:01:52Z cthedot $'
     13
     14  import xml.dom
     15  import cssutils
     16  from cssutils.util import _SimpleNamespaces
     17
     18 -class Selector(cssutils.util.Base2):
     19      """
     20      (cssutils) a single selector in a SelectorList of a CSSStyleRule
     21
     22      Properties
     23      ==========
     24      element
     25          Effective element target of this selector
     26      parentList: of type SelectorList, readonly
     27          The SelectorList that contains this selector or None if this
     28          Selector is not attached to a SelectorList.
     29      selectorText
     30          textual representation of this Selector
     31      seq
     32          sequence of Selector parts including comments
     33      specificity (READONLY)
     34          tuple of (a, b, c, d) where:
     35
     36          a
     37              presence of style in document, always 0 if not used on a document
     38          b
     39              number of ID selectors
     40          c
     41              number of .class selectors
     42          d
     43              number of Element (type) selectors
     44
     45      wellformed
     46          if this selector is wellformed regarding the Selector spec
     47
     48      Format
     49      ======
     50      ::
     51
     52          # implemented in SelectorList
     53          selectors_group
     54            : selector [ COMMA S* selector ]*
     55            ;
     56
     57          selector
     58            : simple_selector_sequence [ combinator simple_selector_sequence ]*
     59            ;
     60
     61          combinator
     62            /* combinators can be surrounded by white space */
     63            : PLUS S* | GREATER S* | TILDE S* | S+
     64            ;
     65
     66          simple_selector_sequence
     67            : [ type_selector | universal ]
     68              [ HASH | class | attrib | pseudo | negation ]*
     69            | [ HASH | class | attrib | pseudo | negation ]+
     70            ;
     71
     72          type_selector
     73            : [ namespace_prefix ]? element_name
     74            ;
     75
     76          namespace_prefix
     77            : [ IDENT | '*' ]? '|'
     78            ;
     79
     80          element_name
     81            : IDENT
     82            ;
     83
     84          universal
     85            : [ namespace_prefix ]? '*'
     86            ;
     87
     88          class
     89            : '.' IDENT
     90            ;
     91
     92          attrib
     93            : '[' S* [ namespace_prefix ]? IDENT S*
     94                  [ [ PREFIXMATCH |
     95                      SUFFIXMATCH |
     96                      SUBSTRINGMATCH |
     97                      '=' |
     98                      INCLUDES |
     99                      DASHMATCH ] S* [ IDENT | STRING ] S*
    100                  ]? ']'
    101            ;
    102
    103          pseudo
    104            /* '::' starts a pseudo-element, ':' a pseudo-class */
    105            /* Exceptions: :first-line, :first-letter, :before and :after. */
    106            /* Note that pseudo-elements are restricted to one per selector and */
    107            /* occur only in the last simple_selector_sequence. */
    108            : ':' ':'? [ IDENT | functional_pseudo ]
    109            ;
    110
    111          functional_pseudo
    112            : FUNCTION S* expression ')'
    113            ;
    114
    115          expression
    116            /* In CSS3, the expressions are identifiers, strings, */
    117            /* or of the form "an+b" */
    118            : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
    119            ;
    120
    121          negation
    122            : NOT S* negation_arg S* ')'
    123            ;
    124
    125          negation_arg
    126            : type_selector | universal | HASH | class | attrib | pseudo
    127            ;
    128
    129      """
    130 -    def __init__(self, selectorText=None, parentList=None,
    131                   readonly=False):
    132          """
    133          :Parameters:
    134              selectorText
    135                  initial value of this selector
    136              parentList
    137                  a SelectorList
    138              readonly
    139                  default to False
    140          """
    141          super(Selector, self).__init__()
    142
    143          self.__namespaces = _SimpleNamespaces(log=self._log)
    144          self._element = None
    145          self._parent = parentList
    146          self._specificity = (0, 0, 0, 0)
    147
    148          if selectorText:
    149              self.selectorText = selectorText
    150
    151          self._readonly = readonly
    152
    153 -    def __getNamespaces(self):
    154          "uses own namespaces if not attached to a sheet, else the sheet's ones"
    155          try:
    156              return self._parent.parentRule.parentStyleSheet.namespaces
    157          except AttributeError:
    158              return self.__namespaces
    159
    160      _namespaces = property(__getNamespaces, doc="""if this Selector is attached
    161          to a CSSStyleSheet the namespaces of that sheet are mirrored here.
    162          While the Selector (or parent SelectorList or parentRule(s) of that are
    163          not attached a own dict of {prefix: namespaceURI} is used.""")
    164
    165
    166      element = property(lambda self: self._element,
    167                         doc=u"Effective element target of this selector.")
    168
    169      parentList = property(lambda self: self._parent,
    170          doc="(DOM) The SelectorList that contains this Selector or\
    171          None if this Selector is not attached to a SelectorList.")
    172
    173 -    def _getSelectorText(self):
    174          """
    175          returns serialized format
    176          """
    177          return cssutils.ser.do_css_Selector(self)
    178
    179 -    def _setSelectorText(self, selectorText):
    180          """
    181          :param selectorText:
    182              parsable string or a tuple of (selectorText, dict-of-namespaces).
    183              Given namespaces are ignored if this object is attached to a
    184              CSSStyleSheet!
    185
    186          :Exceptions:
    187              - `NAMESPACE_ERR`: (self)
    188                Raised if the specified selector uses an unknown namespace
    189                prefix.
    190              - `SYNTAX_ERR`: (self)
    191                Raised if the specified CSS string value has a syntax error
    192                and is unparsable.
    193              - `NO_MODIFICATION_ALLOWED_ERR`: (self)
    194                Raised if this rule is readonly.
    195          """
    196          self._checkReadonly()
    197
    198          # might be (selectorText, namespaces)
    199          selectorText, namespaces = self._splitNamespacesOff(selectorText)
    200
    201          try:
    202              # uses parent stylesheets namespaces if available, otherwise given ones
    203              namespaces = self.parentList.parentRule.parentStyleSheet.namespaces
    204          except AttributeError:
    205              pass
    206          tokenizer = self._tokenize2(selectorText)
    207          if not tokenizer:
    208              self._log.error(u'Selector: No selectorText given.')
    209          else:
    210              # prepare tokenlist:
    211              #     "*" -> type "universal"
    212              #     "*"|IDENT + "|" -> combined to "namespace_prefix"
    213              #     "|" -> type "namespace_prefix"
    214              #     "." + IDENT -> combined to "class"
    215              #     ":" + IDENT, ":" + FUNCTION -> pseudo-class
    216              #     FUNCTION "not(" -> negation
    217              #     "::" + IDENT, "::" + FUNCTION -> pseudo-element
    218              tokens = []
    219              for t in tokenizer:
    220                  typ, val, lin, col = t
    221                  if val == u':' and tokens and\
    222                     self._tokenvalue(tokens[-1]) == ':':
    223                      # combine ":" and ":"
    224                      tokens[-1] = (typ, u'::', lin, col)
    225
    226                  elif typ == 'IDENT' and tokens\
    227                       and self._tokenvalue(tokens[-1]) == u'.':
    228                      # class: combine to .IDENT
    229                      tokens[-1] = ('class', u'.'+val, lin, col)
    230                  elif typ == 'IDENT' and tokens and \
    231                       self._tokenvalue(tokens[-1]).startswith(u':') and\
    232                       not self._tokenvalue(tokens[-1]).endswith(u'('):
    233                      # pseudo-X: combine to :IDENT or ::IDENT but not ":a(" + "b"
    234                      if self._tokenvalue(tokens[-1]).startswith(u'::'):
    235                          t = 'pseudo-element'
    236                      else:
    237                          t = 'pseudo-class'
    238                      tokens[-1] = (t, self._tokenvalue(tokens[-1])+val, lin, col)
    239
    240                  elif typ == 'FUNCTION' and val == u'not(' and tokens and \
    241                       u':' == self._tokenvalue(tokens[-1]):
    242                      tokens[-1] = ('negation', u':' + val, lin, tokens[-1][3])
    243                  elif typ == 'FUNCTION' and tokens\
    244                       and self._tokenvalue(tokens[-1]).startswith(u':'):
    245                      # pseudo-X: combine to :FUNCTION( or ::FUNCTION(
    246                      if self._tokenvalue(tokens[-1]).startswith(u'::'):
    247                          t = 'pseudo-element'
    248                      else:
    249                          t = 'pseudo-class'
    250                      tokens[-1] = (t, self._tokenvalue(tokens[-1])+val, lin, col)
    251
    252                  elif val == u'*' and tokens and\
    253                       self._type(tokens[-1]) == 'namespace_prefix' and\
    254                       self._tokenvalue(tokens[-1]).endswith(u'|'):
    255                      # combine prefix|*
    256                      tokens[-1] = ('universal', self._tokenvalue(tokens[-1])+val,
    257                                    lin, col)
    258                  elif val == u'*':
    259                      # universal: "*"
    260                      tokens.append(('universal', val, lin, col))
    261
    262                  elif val == u'|' and tokens and\
    263                       self._type(tokens[-1]) in (self._prods.IDENT, 'universal') and\
    264                       self._tokenvalue(tokens[-1]).find(u'|') == -1:
    265                      # namespace_prefix: "IDENT|" or "*|"
    266                      tokens[-1] = ('namespace_prefix',
    267                                    self._tokenvalue(tokens[-1])+u'|', lin, col)
    268                  elif val == u'|':
    269                      # namespace_prefix: "|"
    270                      tokens.append(('namespace_prefix', val, lin, col))
    271
    272                  else:
    273                      tokens.append(t)
    274
    275              # TODO: back to generator but not elegant at all!
    276              tokenizer = (t for t in tokens)
    277
    278              # for closures: must be a mutable
    279              new = {'context': [''], # stack of: 'attrib', 'negation', 'pseudo'
    280                     'element': None,
    281                     '_PREFIX': None,
    282                     'specificity': [0, 0, 0, 0], # mutable, finally a tuple!
    283                     'wellformed': True
    284                     }
    285              # used for equality checks and setting of a space combinator
    286              S = u' '
    287
    288              def append(seq, val, typ=None, token=None):
    289                  """
    290                  appends to seq
    291
    292                  namespace_prefix, IDENT will be combined to a tuple
    293                  (prefix, name) where prefix might be None, the empty string
    294                  or a prefix.
    295
    296                  Saved are also:
    297                      - specificity definition: style, id, class/att, type
    298                      - element: the element this Selector is for
    299                  """
    300                  context = new['context'][-1]
    301                  if token:
    302                      line, col = token[2], token[3]
    303                  else:
    304                      line, col = None, None
    305
    306                  if typ == '_PREFIX':
    307                      # SPECIAL TYPE: save prefix for combination with next
    308                      new['_PREFIX'] = val[:-1]
    309                      # handle next time
    310                      return
    311
    312                  if new['_PREFIX'] is not None:
    313                      # as saved from before and reset to None
    314                      prefix, new['_PREFIX'] = new['_PREFIX'], None
    315                  elif typ == 'universal' and '|' in val:
    316                      # val == *|* or prefix|*
    317                      prefix, val = val.split('|')
    318                  else:
    319                      prefix = None
    320
    321                  # namespace
    322                  if (typ.endswith('-selector') or typ == 'universal') and not (
    323                      'attribute-selector' == typ and not prefix):
    324                      # att **IS NOT** in default ns
    325                      if prefix == u'*':
    326                          # *|name: in ANY_NS
    327                          namespaceURI = cssutils._ANYNS
    328                      elif prefix is None:
    329                          # e or *: default namespace with prefix u'' or local-name()
    330                          namespaceURI = namespaces.get(u'', None)
    331                      elif prefix == u'':
    332                          # |name or |*: in no (or the empty) namespace
    333                          namespaceURI = u''
    334                      else:
    335                          # explicit namespace prefix
    336                          # does not raise KeyError, see _SimpleNamespaces
    337                          namespaceURI = namespaces[prefix]
    338
    339                          if namespaceURI is None:
    340                              new['wellformed'] = False
    341                              self._log.error(
    342                                  u'Selector: No namespaceURI found for prefix %r' %
    343                                  prefix, token=token, error=xml.dom.NamespaceErr)
    344                              return
    345
    346                      # val is now (namespaceprefix, name) tuple
    347                      val = (namespaceURI, val)
    348
    349                  # specificity
    350                  if not context or context == 'negation':
    351                      if 'id' == typ:
    352                          new['specificity'][1] += 1
    353                      elif 'class' == typ or '[' == val:
    354                          new['specificity'][2] += 1
    355                      elif typ in ('type-selector', 'negation-type-selector',
    356                                    'pseudo-element'):
    357                          new['specificity'][3] += 1
    358                  if not context and typ in ('type-selector', 'universal'):
    359                      # define element
    360                      new['element'] = val
    361
    362                  seq.append(val, typ, line=line, col=col)
    363
    364              # expected constants
    365              simple_selector_sequence = 'type_selector universal HASH class attrib pseudo negation '
    366              simple_selector_sequence2 = 'HASH class attrib pseudo negation '
    367
    368              element_name = 'element_name'
    369
    370              negation_arg = 'type_selector universal HASH class attrib pseudo'
    371              negationend = ')'
    372
    373              attname = 'prefix attribute'
    374              attname2 = 'attribute'
    375              attcombinator = 'combinator ]' # optional
    376              attvalue = 'value'       # optional
    377              attend = ']'
    378
    379              expressionstart = 'PLUS - DIMENSION NUMBER STRING IDENT'
    380              expression = expressionstart + ' )'
    381
    382              combinator = ' combinator'
    383
    384              def _COMMENT(expected, seq, token, tokenizer=None):
    385                  "special implementation for comment token"
    386                  append(seq, cssutils.css.CSSComment([token]), 'COMMENT',
    387                         token=token)
    388                  return expected
    389
    390              def _S(expected, seq, token, tokenizer=None):
    391                  # S
    392                  context = new['context'][-1]
    393                  if context.startswith('pseudo-'):
    394                      if seq and seq[-1].value not in u'+-':
    395                          # e.g. x:func(a + b)
    396                          append(seq, S, 'S', token=token)
    397                      return expected
    398
    399                  elif context != 'attrib' and 'combinator' in expected:
    400                      append(seq, S, 'descendant', token=token)
    401                      return simple_selector_sequence + combinator
    402
    403                  else:
    404                      return expected
    405
    406              def _universal(expected, seq, token, tokenizer=None):
    407                  # *|* or prefix|*
    408                  context = new['context'][-1]
    409                  val = self._tokenvalue(token)
    410                  if 'universal' in expected:
    411                      append(seq, val, 'universal', token=token)
    412
    413                      if 'negation' == context:
    414                          return negationend
    415                      else:
    416                          return simple_selector_sequence2 + combinator
    417
    418                  else:
    419                      new['wellformed'] = False
    420                      self._log.error(
    421                          u'Selector: Unexpected universal.', token=token)
    422                      return expected
    423
    424              def _namespace_prefix(expected, seq, token, tokenizer=None):
    425                  # prefix| => element_name
    426                  # or prefix| => attribute_name if attrib
    427                  context = new['context'][-1]
    428                  val = self._tokenvalue(token)
    429                  if 'attrib' == context and 'prefix' in expected:
    430                      # [PREFIX|att]
    431                      append(seq, val, '_PREFIX', token=token)
    432                      return attname2
    433                  elif 'type_selector' in expected:
    434                      # PREFIX|*
    435                      append(seq, val, '_PREFIX', token=token)
    436                      return element_name
    437                  else:
    438                      new['wellformed'] = False
    439                      self._log.error(
    440                          u'Selector: Unexpected namespace prefix.', token=token)
    441                      return expected
    442
    443              def _pseudo(expected, seq, token, tokenizer=None):
    444                  # pseudo-class or pseudo-element :a ::a :a( ::a(
    445                  """
    446                  /* '::' starts a pseudo-element, ':' a pseudo-class */
    447                  /* Exceptions: :first-line, :first-letter, :before and :after. */
    448                  /* Note that pseudo-elements are restricted to one per selector and */
    449                  /* occur only in the last simple_selector_sequence. */
    450                  """
    451                  context = new['context'][-1]
    452                  val, typ = self._tokenvalue(token, normalize=True), self._type(token)
    453                  if 'pseudo' in expected:
    454                      if val in (':first-line', ':first-letter', ':before', ':after'):
    455                          # always pseudo-element ???
    456                          typ = 'pseudo-element'
    457                      append(seq, val, typ, token=token)
    458
    459                      if val.endswith(u'('):
    460                          # function
    461                          new['context'].append(typ) # "pseudo-" "class" or "element"
    462                          return expressionstart
    463                      elif 'negation' == context:
    464                          return negationend
    465                      elif 'pseudo-element' == typ:
    466                          # only one per element, check at ) also!
    467                          return combinator
    468                      else:
    469                          return simple_selector_sequence2 + combinator
    470
    471                  else:
    472                      new['wellformed'] = False
    473                      self._log.error(
    474                          u'Selector: Unexpected start of pseudo.', token=token)
    475                      return expected
    476
    477              def _expression(expected, seq, token, tokenizer=None):
    478                  # [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
    479                  context = new['context'][-1]
    480                  val, typ = self._tokenvalue(token), self._type(token)
    481                  if context.startswith('pseudo-'):
    482                      append(seq, val, typ, token=token)
    483                      return expression
    484                  else:
    485                      new['wellformed'] = False
    486                      self._log.error(
    487                          u'Selector: Unexpected %s.' % typ, token=token)
    488                      return expected
    489
    490              def _attcombinator(expected, seq, token, tokenizer=None):
    491                  # context: attrib
    492                  # PREFIXMATCH | SUFFIXMATCH | SUBSTRINGMATCH | INCLUDES |
    493                  # DASHMATCH
    494                  context = new['context'][-1]
    495                  val, typ = self._tokenvalue(token), self._type(token)
    496                  if 'attrib' == context and 'combinator' in expected:
    497                      # combinator in attrib
    498                      append(seq, val, typ.lower(), token=token)
    499                      return attvalue
    500                  else:
    501                      new['wellformed'] = False
    502                      self._log.error(
    503                          u'Selector: Unexpected %s.' % typ, token=token)
    504                      return expected
    505
    506              def _string(expected, seq, token, tokenizer=None):
    507                  # identifier
    508                  context = new['context'][-1]
    509                  typ, val = self._type(token), self._stringtokenvalue(token)
    510
    511                  # context: attrib
    512                  if 'attrib' == context and 'value' in expected:
    513                      # attrib: [...=VALUE]
    514                      append(seq, val, typ, token=token)
    515                      return attend
    516
    517                  # context: pseudo
    518                  elif context.startswith('pseudo-'):
    519                      # :func(...)
    520                      append(seq, val, typ, token=token)
    521                      return expression
    522
    523                  else:
    524                      new['wellformed'] = False
    525                      self._log.error(
    526                          u'Selector: Unexpected STRING.', token=token)
    527                      return expected
    528
    529              def _ident(expected, seq, token, tokenizer=None):
    530                  # identifier
    531                  context = new['context'][-1]
    532                  val, typ = self._tokenvalue(token), self._type(token)
    533
    534                  # context: attrib
    535                  if 'attrib' == context and 'attribute' in expected:
    536                      # attrib: [...|ATT...]
    537                      append(seq, val, 'attribute-selector', token=token)
    538                      return attcombinator
    539
    540                  elif 'attrib' == context and 'value' in expected:
    541                      # attrib: [...=VALUE]
    542                      append(seq, val, 'attribute-value', token=token)
    543                      return attend
    544
    545                  # context: negation
    546                  elif 'negation' == context:
    547                      # negation: (prefix|IDENT)
    548                      append(seq, val, 'negation-type-selector', token=token)
    549                      return negationend
    550
    551                  # context: pseudo
    552                  elif context.startswith('pseudo-'):
    553                      # :func(...)
    554                      append(seq, val, typ, token=token)
    555                      return expression
    556
    557                  elif 'type_selector' in expected or element_name == expected:
    558                      # element name after ns or complete type_selector
    559                      append(seq, val, 'type-selector', token=token)
    560                      return simple_selector_sequence2 + combinator
    561
    562                  else:
    563                      new['wellformed'] = False
    564                      self._log.error(
    565                          u'Selector: Unexpected IDENT.',
    566                          token=token)
    567                      return expected
    568
    569              def _class(expected, seq, token, tokenizer=None):
    570                  # .IDENT
    571                  context = new['context'][-1]
    572                  val = self._tokenvalue(token)
    573                  if 'class' in expected:
    574                      append(seq, val, 'class', token=token)
    575
    576                      if 'negation' == context:
    577                          return negationend
    578                      else:
    579                          return simple_selector_sequence2 + combinator
    580
    581                  else:
    582                      new['wellformed'] = False
    583                      self._log.error(
    584                          u'Selector: Unexpected class.', token=token)
    585                      return expected
    586
    587              def _hash(expected, seq, token, tokenizer=None):
    588                  # #IDENT
    589                  context = new['context'][-1]
    590                  val = self._tokenvalue(token)
    591                  if 'HASH' in expected:
    592                      append(seq, val, 'id', token=token)
    593
    594                      if 'negation' == context:
    595                          return negationend
    596                      else:
    597                          return simple_selector_sequence2 + combinator
    598
    599                  else:
    600                      new['wellformed'] = False
    601                      self._log.error(
    602                          u'Selector: Unexpected HASH.', token=token)
    603                      return expected
    604
    605              def _char(expected, seq, token, tokenizer=None):
    606                  # + > ~ ) [ ] + -
    607                  context = new['context'][-1]
    608                  val = self._tokenvalue(token)
    609
    610                  # context: attrib
    611                  if u']' == val and 'attrib' == context and ']' in expected:
    612                      # end of attrib
    613                      append(seq, val, 'attribute-end', token=token)
    614                      context = new['context'].pop() # attrib is done
    615                      context = new['context'][-1]
    616                      if 'negation' == context:
    617                          return negationend
    618                      else:
    619                          return simple_selector_sequence2 + combinator
    620
    621                  elif u'=' == val and 'attrib' == context and 'combinator' in expected:
    622                      # combinator in attrib
    623                      append(seq, val, 'equals', token=token)
    624                      return attvalue
    625
    626                  # context: negation
    627                  elif u')' == val and 'negation' == context and u')' in expected:
    628                      # not(negation_arg)"
    629                      append(seq, val, 'negation-end', token=token)
    630                      new['context'].pop() # negation is done
    631                      context = new['context'][-1]
    632                      return simple_selector_sequence + combinator
    633
    634                  # context: pseudo (at least one expression)
    635                  elif val in u'+-' and context.startswith('pseudo-'):
    636                      # :func(+ -)"
    637                      _names = {'+': 'plus', '-': 'minus'}
    638                      if val == u'+' and seq and seq[-1].value == S:
    639                          seq.replace(-1, val, _names[val])
    640                      else:
    641                          append(seq, val, _names[val],
    642                                 token=token)
    643                      return expression
    644
    645                  elif u')' == val and context.startswith('pseudo-') and\
    646                       expression == expected:
    647                      # :func(expression)"
    648                      append(seq, val, 'function-end', token=token)
    649                      new['context'].pop() # pseudo is done
    650                      if 'pseudo-element' == context:
    651                          return combinator
    652                      else:
    653                          return simple_selector_sequence + combinator
    654
    655                  # context: ROOT
    656                  elif u'[' == val and 'attrib' in expected:
    657                      # start of [attrib]
    658                      append(seq, val, 'attribute-start', token=token)
    659                      new['context'].append('attrib')
    660                      return attname
    661
    662                  elif val in u'+>~' and 'combinator' in expected:
    663                      # no other combinator except S may be following
    664                      _names = {
    665                          '>': 'child',
    666                          '+': 'adjacent-sibling',
    667                          '~': 'following-sibling'}
    668                      if seq and seq[-1].value == S:
    669                          seq.replace(-1, val, _names[val])
    670                      else:
    671                          append(seq, val, _names[val], token=token)
    672                      return simple_selector_sequence
    673
    674                  elif u',' == val:
    675                      # not a selectorlist
    676                      new['wellformed'] = False
    677                      self._log.error(
    678                          u'Selector: Single selector only.',
    679                          error=xml.dom.InvalidModificationErr,
    680                          token=token)
    681                      return expected
    682
    683                  else:
    684                      new['wellformed'] = False
    685                      self._log.error(
    686                          u'Selector: Unexpected CHAR.', token=token)
    687                      return expected
    688
    689              def _negation(expected, seq, token, tokenizer=None):
    690                  # not(
    691                  context = new['context'][-1]
    692                  val = self._tokenvalue(token, normalize=True)
    693                  if 'negation' in expected:
    694                      new['context'].append('negation')
    695                      append(seq, val, 'negation-start', token=token)
    696                      return negation_arg
    697                  else:
    698                      new['wellformed'] = False
    699                      self._log.error(
    700                          u'Selector: Unexpected negation.', token=token)
    701                      return expected
    702
    703              # expected: only|not or mediatype, mediatype, feature, and
    704              newseq = self._tempSeq()
    705
    706              wellformed, expected = self._parse(expected=simple_selector_sequence,
    707                  seq=newseq, tokenizer=tokenizer,
    708                  productions={'CHAR': _char,
    709                               'class': _class,
    710                               'HASH': _hash,
    711                               'STRING': _string,
    712                               'IDENT': _ident,
    713                               'namespace_prefix': _namespace_prefix,
    714                               'negation': _negation,
    715                               'pseudo-class': _pseudo,
    716                               'pseudo-element': _pseudo,
    717                               'universal': _universal,
    718                               # pseudo
    719                               'NUMBER': _expression,
    720                               'DIMENSION': _expression,
    721                               # attribute
    722                               'PREFIXMATCH': _attcombinator,
    723                               'SUFFIXMATCH': _attcombinator,
    724                               'SUBSTRINGMATCH': _attcombinator,
    725                               'DASHMATCH': _attcombinator,
    726                               'INCLUDES': _attcombinator,
    727
    728                               'S': _S,
    729                               'COMMENT': _COMMENT})
    730              wellformed = wellformed and new['wellformed']
    731
    732              # post condition
    733              if len(new['context']) > 1 or not newseq:
    734                  wellformed = False
    735                  self._log.error(u'Selector: Invalid or incomplete selector: %s' %
    736                      self._valuestr(selectorText))
    737
    738              if expected == 'element_name':
    739                  wellformed = False
    740                  self._log.error(u'Selector: No element name found: %s' %
    741                      self._valuestr(selectorText))
    742
    743              if expected == simple_selector_sequence and newseq:
    744                  wellformed = False
    745                  self._log.error(u'Selector: Cannot end with combinator: %s' %
    746                      self._valuestr(selectorText))
    747
    748              if newseq and hasattr(newseq[-1].value, 'strip') and \
    749                 newseq[-1].value.strip() == u'':
    750                  del newseq[-1]
    751
    752              # set
    753              if wellformed:
    754                  self.__namespaces = namespaces
    755                  self._element = new['element']
    756                  self._specificity = tuple(new['specificity'])
    757                  self._setSeq(newseq)
    758                  # filter that only used ones are kept
    759                  self.__namespaces = self._getUsedNamespaces()
    760
    761      selectorText = property(_getSelectorText, _setSelectorText,
    762          doc="(DOM) The parsable textual representation of the selector.")
    763
    764
    765      specificity = property(lambda self: self._specificity,
    766                             doc="Specificity of this selector (READONLY).")
    767
    768      wellformed = property(lambda self: bool(len(self.seq)))
    769
    770 -    def __repr__(self):
    771          if self.__getNamespaces():
    772              st = (self.selectorText, self._getUsedNamespaces())
    773          else:
    774              st = self.selectorText
    775          return u"cssutils.css.%s(selectorText=%r)" % (
    776                  self.__class__.__name__, st)
    777
    778 -    def __str__(self):
    779          return u"<cssutils.css.%s object selectorText=%r specificity=%r _namespaces=%r at 0x%x>" % (
    780                  self.__class__.__name__, self.selectorText, self.specificity,
    781                  self._getUsedNamespaces(), id(self))
    782
    783 -    def _getUsedUris(self):
    784          "returns list of actually used URIs in this Selector"
    785          uris = set()
    786          for item in self.seq:
    787              type_, val = item.type, item.value
    788              if type_.endswith(u'-selector') or type_ == u'universal' and \
    789                 type(val) == tuple and val[0] not in (None, u'*'):
    790                  uris.add(val[0])
    791          return uris
    792
    793 -    def _getUsedNamespaces(self):
    794          "returns actually used namespaces only"
    795          useduris = self._getUsedUris()
    796          namespaces = _SimpleNamespaces(log=self._log)
    797          for p, uri in self._namespaces.items():
    798              if uri in useduris:
    799                  namespaces[p] = uri
    800          return namespaces
    801