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