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 1116 2008-03-05 13:52:23Z 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()
    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          try:
    201              # uses parent stylesheets namespaces if available, otherwise given ones
    202              namespaces = self.parentList.parentRule.parentStyleSheet.namespaces
    203          except AttributeError:
    204              pass
    205          tokenizer = self._tokenize2(selectorText)
    206          if not tokenizer:
    207              self._log.error(u'Selector: No selectorText given.')
    208          else:
    209              # prepare tokenlist:
    210              #     "*" -> type "universal"
    211              #     "*"|IDENT + "|" -> combined to "namespace_prefix"
    212              #     "|" -> type "namespace_prefix"
    213              #     "." + IDENT -> combined to "class"
    214              #     ":" + IDENT, ":" + FUNCTION -> pseudo-class
    215              #     FUNCTION "not(" -> negation
    216              #     "::" + IDENT, "::" + FUNCTION -> pseudo-element
    217              tokens = []
    218              for t in tokenizer:
    219                  typ, val, lin, col = t
    220                  if val == u':' and tokens and\
    221                     self._tokenvalue(tokens[-1]) == ':':
    222                      # combine ":" and ":"
    223                      tokens[-1] = (typ, u'::', lin, col)
    224
    225                  elif typ == 'IDENT' and tokens\
    226                       and self._tokenvalue(tokens[-1]) == u'.':
    227                      # class: combine to .IDENT
    228                      tokens[-1] = ('class', u'.'+val, lin, col)
    229                  elif typ == 'IDENT' and tokens and \
    230                       self._tokenvalue(tokens[-1]).startswith(u':') and\
    231                       not self._tokenvalue(tokens[-1]).endswith(u'('):
    232                      # pseudo-X: combine to :IDENT or ::IDENT but not ":a(" + "b"
    233                      if self._tokenvalue(tokens[-1]).startswith(u'::'):
    234                          t = 'pseudo-element'
    235                      else:
    236                          t = 'pseudo-class'
    237                      tokens[-1] = (t, self._tokenvalue(tokens[-1])+val, lin, col)
    238
    239                  elif typ == 'FUNCTION' and val == u'not(' and tokens and \
    240                       u':' == self._tokenvalue(tokens[-1]):
    241                      tokens[-1] = ('negation', u':' + val, lin, tokens[-1][3])
    242                  elif typ == 'FUNCTION' and tokens\
    243                       and self._tokenvalue(tokens[-1]).startswith(u':'):
    244                      # pseudo-X: combine to :FUNCTION( or ::FUNCTION(
    245                      if self._tokenvalue(tokens[-1]).startswith(u'::'):
    246                          t = 'pseudo-element'
    247                      else:
    248                          t = 'pseudo-class'
    249                      tokens[-1] = (t, self._tokenvalue(tokens[-1])+val, lin, col)
    250
    251                  elif val == u'*' and tokens and\
    252                       self._type(tokens[-1]) == 'namespace_prefix' and\
    253                       self._tokenvalue(tokens[-1]).endswith(u'|'):
    254                      # combine prefix|*
    255                      tokens[-1] = ('universal', self._tokenvalue(tokens[-1])+val,
    256                                    lin, col)
    257                  elif val == u'*':
    258                      # universal: "*"
    259                      tokens.append(('universal', val, lin, col))
    260
    261                  elif val == u'|' and tokens and\
    262                       self._type(tokens[-1]) in (self._prods.IDENT, 'universal') and\
    263                       self._tokenvalue(tokens[-1]).find(u'|') == -1:
    264                      # namespace_prefix: "IDENT|" or "*|"
    265                      tokens[-1] = ('namespace_prefix',
    266                                    self._tokenvalue(tokens[-1])+u'|', lin, col)
    267                  elif val == u'|':
    268                      # namespace_prefix: "|"
    269                      tokens.append(('namespace_prefix', val, lin, col))
    270
    271                  else:
    272                      tokens.append(t)
    273
    274              # TODO: back to generator but not elegant at all!
    275              tokenizer = (t for t in tokens)
    276
    277              # for closures: must be a mutable
    278              new = {'context': [''], # stack of: 'attrib', 'negation', 'pseudo'
    279                     'element': None,
    280                     '_PREFIX': None,
    281                     'specificity': [0, 0, 0, 0], # mutable, finally a tuple!
    282                     'wellformed': True
    283                     }
    284              # used for equality checks and setting of a space combinator
    285              S = u' '
    286
    287              def append(seq, val, typ=None, token=None):
    288                  """
    289                  appends to seq
    290
    291                  namespace_prefix, IDENT will be combined to a tuple
    292                  (prefix, name) where prefix might be None, the empty string
    293                  or a prefix.
    294
    295                  Saved are also:
    296                      - specificity definition: style, id, class/att, type
    297                      - element: the element this Selector is for
    298                  """
    299                  context = new['context'][-1]
    300                  if token:
    301                      line, col = token[2], token[3]
    302                  else:
    303                      line, col = None, None
    304
    305                  if typ == '_PREFIX':
    306                      # SPECIAL TYPE: save prefix for combination with next
    307                      new['_PREFIX'] = val[:-1]
    308                      # handle next time
    309                      return
    310
    311                  if new['_PREFIX'] is not None:
    312                      # as saved from before and reset to None
    313                      prefix, new['_PREFIX'] = new['_PREFIX'], None
    314                  elif typ == 'universal' and '|' in val:
    315                      # val == *|* or prefix|*
    316                      prefix, val = val.split('|')
    317                  else:
    318                      prefix = None
    319
    320                  # namespace
    321                  if (typ.endswith('-selector') or typ == 'universal') and not (
    322                      'attribute-selector' == typ and not prefix):
    323                      # att **IS NOT** in default ns
    324                      if prefix == u'*':
    325                          # *|name: in ANY_NS
    326                          namespaceURI = cssutils._ANYNS
    327                      elif prefix is None:
    328                          # e or *: default namespace with prefix u'' or local-name()
    329                          namespaceURI = namespaces.get(u'', None)
    330                      elif prefix == u'':
    331                          # |name or |*: in no (or the empty) namespace
    332                          namespaceURI = u''
    333                      else:
    334                          # explicit namespace prefix
    335                          try:
    336                              namespaceURI = namespaces[prefix]
    337                          except KeyError:
    338                              new['wellformed'] = False
    339                              self._log.error(
    340                                  u'Selector: No namespaceURI found for prefix %r' %
    341                                  prefix, token=token, error=xml.dom.NamespaceErr)
    342                              return
    343
    344                      # val is now (namespaceprefix, name) tuple
    345                      val = (namespaceURI, val)
    346
    347                  # specificity
    348                  if not context or context == 'negation':
    349                      if 'id' == typ:
    350                          new['specificity'][1] += 1
    351                      elif 'class' == typ or '[' == val:
    352                          new['specificity'][2] += 1
    353                      elif typ in ('type-selector', 'negation-type-selector',
    354                                    'pseudo-element'):
    355                          new['specificity'][3] += 1
    356                  if not context and typ in ('type-selector', 'universal'):
    357                      # define element
    358                      new['element'] = val
    359
    360                  seq.append(val, typ, line=line, col=col)
    361
    362              # expected constants
    363              simple_selector_sequence = 'type_selector universal HASH class attrib pseudo negation '
    364              simple_selector_sequence2 = 'HASH class attrib pseudo negation '
    365
    366              element_name = 'element_name'
    367
    368              negation_arg = 'type_selector universal HASH class attrib pseudo'
    369              negationend = ')'
    370
    371              attname = 'prefix attribute'
    372              attname2 = 'attribute'
    373              attcombinator = 'combinator ]' # optional
    374              attvalue = 'value'       # optional
    375              attend = ']'
    376
    377              expressionstart = 'PLUS - DIMENSION NUMBER STRING IDENT'
    378              expression = expressionstart + ' )'
    379
    380              combinator = ' combinator'
    381
    382              def _COMMENT(expected, seq, token, tokenizer=None):
    383                  "special implementation for comment token"
    384                  append(seq, cssutils.css.CSSComment([token]), 'COMMENT',
    385                         token=token)
    386                  return expected
    387
    388              def _S(expected, seq, token, tokenizer=None):
    389                  # S
    390                  context = new['context'][-1]
    391                  if context.startswith('pseudo-'):
    392                      if seq and seq[-1].value not in u'+-':
    393                          # e.g. x:func(a + b)
    394                          append(seq, S, 'S', token=token)
    395                      return expected
    396
    397                  elif context != 'attrib' and 'combinator' in expected:
    398                      append(seq, S, 'descendant', token=token)
    399                      return simple_selector_sequence + combinator
    400
    401                  else:
    402                      return expected
    403
    404              def _universal(expected, seq, token, tokenizer=None):
    405                  # *|* or prefix|*
    406                  context = new['context'][-1]
    407                  val = self._tokenvalue(token)
    408                  if 'universal' in expected:
    409                      append(seq, val, 'universal', token=token)
    410
    411                      if 'negation' == context:
    412                          return negationend
    413                      else:
    414                          return simple_selector_sequence2 + combinator
    415
    416                  else:
    417                      new['wellformed'] = False
    418                      self._log.error(
    419                          u'Selector: Unexpected universal.', token=token)
    420                      return expected
    421
    422              def _namespace_prefix(expected, seq, token, tokenizer=None):
    423                  # prefix| => element_name
    424                  # or prefix| => attribute_name if attrib
    425                  context = new['context'][-1]
    426                  val = self._tokenvalue(token)
    427                  if 'attrib' == context and 'prefix' in expected:
    428                      # [PREFIX|att]
    429                      append(seq, val, '_PREFIX', token=token)
    430                      return attname2
    431                  elif 'type_selector' in expected:
    432                      # PREFIX|*
    433                      append(seq, val, '_PREFIX', token=token)
    434                      return element_name
    435                  else:
    436                      new['wellformed'] = False
    437                      self._log.error(
    438                          u'Selector: Unexpected namespace prefix.', token=token)
    439                      return expected
    440
    441              def _pseudo(expected, seq, token, tokenizer=None):
    442                  # pseudo-class or pseudo-element :a ::a :a( ::a(
    443                  """
    444                  /* '::' starts a pseudo-element, ':' a pseudo-class */
    445                  /* Exceptions: :first-line, :first-letter, :before and :after. */
    446                  /* Note that pseudo-elements are restricted to one per selector and */
    447                  /* occur only in the last simple_selector_sequence. */
    448                  """
    449                  context = new['context'][-1]
    450                  val, typ = self._tokenvalue(token, normalize=True), self._type(token)
    451                  if 'pseudo' in expected:
    452                      if val in (':first-line', ':first-letter', ':before', ':after'):
    453                          # always pseudo-element ???
    454                          typ = 'pseudo-element'
    455                      append(seq, val, typ, token=token)
    456
    457                      if val.endswith(u'('):
    458                          # function
    459                          new['context'].append(typ) # "pseudo-" "class" or "element"
    460                          return expressionstart
    461                      elif 'negation' == context:
    462                          return negationend
    463                      elif 'pseudo-element' == typ:
    464                          # only one per element, check at ) also!
    465                          return combinator
    466                      else:
    467                          return simple_selector_sequence2 + combinator
    468
    469                  else:
    470                      new['wellformed'] = False
    471                      self._log.error(
    472                          u'Selector: Unexpected start of pseudo.', token=token)
    473                      return expected
    474
    475              def _expression(expected, seq, token, tokenizer=None):
    476                  # [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
    477                  context = new['context'][-1]
    478                  val, typ = self._tokenvalue(token), self._type(token)
    479                  if context.startswith('pseudo-'):
    480                      append(seq, val, typ, token=token)
    481                      return expression
    482                  else:
    483                      new['wellformed'] = False
    484                      self._log.error(
    485                          u'Selector: Unexpected %s.' % typ, token=token)
    486                      return expected
    487
    488              def _attcombinator(expected, seq, token, tokenizer=None):
    489                  # context: attrib
    490                  # PREFIXMATCH | SUFFIXMATCH | SUBSTRINGMATCH | INCLUDES |
    491                  # DASHMATCH
    492                  context = new['context'][-1]
    493                  val, typ = self._tokenvalue(token), self._type(token)
    494                  if 'attrib' == context and 'combinator' in expected:
    495                      # combinator in attrib
    496                      append(seq, val, typ.lower(), token=token)
    497                      return attvalue
    498                  else:
    499                      new['wellformed'] = False
    500                      self._log.error(
    501                          u'Selector: Unexpected %s.' % typ, token=token)
    502                      return expected
    503
    504              def _string(expected, seq, token, tokenizer=None):
    505                  # identifier
    506                  context = new['context'][-1]
    507                  typ, val = self._type(token), self._stringtokenvalue(token)
    508
    509                  # context: attrib
    510                  if 'attrib' == context and 'value' in expected:
    511                      # attrib: [...=VALUE]
    512                      append(seq, val, typ, token=token)
    513                      return attend
    514
    515                  # context: pseudo
    516                  elif context.startswith('pseudo-'):
    517                      # :func(...)
    518                      append(seq, val, typ, token=token)
    519                      return expression
    520
    521                  else:
    522                      new['wellformed'] = False
    523                      self._log.error(
    524                          u'Selector: Unexpected STRING.', token=token)
    525                      return expected
    526
    527              def _ident(expected, seq, token, tokenizer=None):
    528                  # identifier
    529                  context = new['context'][-1]
    530                  val, typ = self._tokenvalue(token), self._type(token)
    531
    532                  # context: attrib
    533                  if 'attrib' == context and 'attribute' in expected:
    534                      # attrib: [...|ATT...]
    535                      append(seq, val, 'attribute-selector', token=token)
    536                      return attcombinator
    537
    538                  elif 'attrib' == context and 'value' in expected:
    539                      # attrib: [...=VALUE]
    540                      append(seq, val, 'attribute-value', token=token)
    541                      return attend
    542
    543                  # context: negation
    544                  elif 'negation' == context:
    545                      # negation: (prefix|IDENT)
    546                      append(seq, val, 'negation-type-selector', token=token)
    547                      return negationend
    548
    549                  # context: pseudo
    550                  elif context.startswith('pseudo-'):
    551                      # :func(...)
    552                      append(seq, val, typ, token=token)
    553                      return expression
    554
    555                  elif 'type_selector' in expected or element_name == expected:
    556                      # element name after ns or complete type_selector
    557                      append(seq, val, 'type-selector', token=token)
    558                      return simple_selector_sequence2 + combinator
    559
    560                  else:
    561                      new['wellformed'] = False
    562                      self._log.error(
    563                          u'Selector: Unexpected IDENT.',
    564                          token=token)
    565                      return expected
    566
    567              def _class(expected, seq, token, tokenizer=None):
    568                  # .IDENT
    569                  context = new['context'][-1]
    570                  val = self._tokenvalue(token)
    571                  if 'class' in expected:
    572                      append(seq, val, 'class', token=token)
    573
    574                      if 'negation' == context:
    575                          return negationend
    576                      else:
    577                          return simple_selector_sequence2 + combinator
    578
    579                  else:
    580                      new['wellformed'] = False
    581                      self._log.error(
    582                          u'Selector: Unexpected class.', token=token)
    583                      return expected
    584
    585              def _hash(expected, seq, token, tokenizer=None):
    586                  # #IDENT
    587                  context = new['context'][-1]
    588                  val = self._tokenvalue(token)
    589                  if 'HASH' in expected:
    590                      append(seq, val, 'id', token=token)
    591
    592                      if 'negation' == context:
    593                          return negationend
    594                      else:
    595                          return simple_selector_sequence2 + combinator
    596
    597                  else:
    598                      new['wellformed'] = False
    599                      self._log.error(
    600                          u'Selector: Unexpected HASH.', token=token)
    601                      return expected
    602
    603              def _char(expected, seq, token, tokenizer=None):
    604                  # + > ~ ) [ ] + -
    605                  context = new['context'][-1]
    606                  val = self._tokenvalue(token)
    607
    608                  # context: attrib
    609                  if u']' == val and 'attrib' == context and ']' in expected:
    610                      # end of attrib
    611                      append(seq, val, 'attribute-end', token=token)
    612                      context = new['context'].pop() # attrib is done
    613                      context = new['context'][-1]
    614                      if 'negation' == context:
    615                          return negationend
    616                      else:
    617                          return simple_selector_sequence2 + combinator
    618
    619                  elif u'=' == val and 'attrib' == context and 'combinator' in expected:
    620                      # combinator in attrib
    621                      append(seq, val, 'equals', token=token)
    622                      return attvalue
    623
    624                  # context: negation
    625                  elif u')' == val and 'negation' == context and u')' in expected:
    626                      # not(negation_arg)"
    627                      append(seq, val, 'negation-end', token=token)
    628                      new['context'].pop() # negation is done
    629                      context = new['context'][-1]
    630                      return simple_selector_sequence + combinator
    631
    632                  # context: pseudo (at least one expression)
    633                  elif val in u'+-' and context.startswith('pseudo-'):
    634                      # :func(+ -)"
    635                      _names = {'+': 'plus', '-': 'minus'}
    636                      if val == u'+' and seq and seq[-1].value == S:
    637                          seq.replace(-1, val, _names[val])
    638                      else:
    639                          append(seq, val, _names[val],
    640                                 token=token)
    641                      return expression
    642
    643                  elif u')' == val and context.startswith('pseudo-') and\
    644                       expression == expected:
    645                      # :func(expression)"
    646                      append(seq, val, 'function-end', token=token)
    647                      new['context'].pop() # pseudo is done
    648                      if 'pseudo-element' == context:
    649                          return combinator
    650                      else:
    651                          return simple_selector_sequence + combinator
    652
    653                  # context: ROOT
    654                  elif u'[' == val and 'attrib' in expected:
    655                      # start of [attrib]
    656                      append(seq, val, 'attribute-start', token=token)
    657                      new['context'].append('attrib')
    658                      return attname
    659
    660                  elif val in u'+>~' and 'combinator' in expected:
    661                      # no other combinator except S may be following
    662                      _names = {
    663                          '>': 'child',
    664                          '+': 'adjacent-sibling',
    665                          '~': 'following-sibling'}
    666                      if seq and seq[-1].value == S:
    667                          seq.replace(-1, val, _names[val])
    668                      else:
    669                          append(seq, val, _names[val], token=token)
    670                      return simple_selector_sequence
    671
    672                  elif u',' == val:
    673                      # not a selectorlist
    674                      new['wellformed'] = False
    675                      self._log.error(
    676                          u'Selector: Single selector only.',
    677                          error=xml.dom.InvalidModificationErr,
    678                          token=token)
    679
    680                  else:
    681                      new['wellformed'] = False
    682                      self._log.error(
    683                          u'Selector: Unexpected CHAR.', token=token)
    684                      return expected
    685
    686              def _negation(expected, seq, token, tokenizer=None):
    687                  # not(
    688                  context = new['context'][-1]
    689                  val = self._tokenvalue(token, normalize=True)
    690                  if 'negation' in expected:
    691                      new['context'].append('negation')
    692                      append(seq, val, 'negation-start', token=token)
    693                      return negation_arg
    694                  else:
    695                      new['wellformed'] = False
    696                      self._log.error(
    697                          u'Selector: Unexpected negation.', token=token)
    698                      return expected
    699
    700              # expected: only|not or mediatype, mediatype, feature, and
    701              newseq = self._tempSeq()
    702
    703              wellformed, expected = self._parse(expected=simple_selector_sequence,
    704                  seq=newseq, tokenizer=tokenizer,
    705                  productions={'CHAR': _char,
    706                               'class': _class,
    707                               'HASH': _hash,
    708                               'STRING': _string,
    709                               'IDENT': _ident,
    710                               'namespace_prefix': _namespace_prefix,
    711                               'negation': _negation,
    712                               'pseudo-class': _pseudo,
    713                               'pseudo-element': _pseudo,
    714                               'universal': _universal,
    715                               # pseudo
    716                               'NUMBER': _expression,
    717                               'DIMENSION': _expression,
    718                               # attribute
    719                               'PREFIXMATCH': _attcombinator,
    720                               'SUFFIXMATCH': _attcombinator,
    721                               'SUBSTRINGMATCH': _attcombinator,
    722                               'DASHMATCH': _attcombinator,
    723                               'INCLUDES': _attcombinator,
    724
    725                               'S': _S,
    726                               'COMMENT': _COMMENT})
    727              wellformed = wellformed and new['wellformed']
    728
    729              # post condition
    730              if len(new['context']) > 1:
    731                  wellformed = False
    732                  self._log.error(u'Selector: Incomplete selector: %s' %
    733                      self._valuestr(selectorText))
    734
    735              if expected == 'element_name':
    736                  wellformed = False
    737                  self._log.error(u'Selector: No element name found: %s' %
    738                      self._valuestr(selectorText))
    739
    740              if expected == simple_selector_sequence:
    741                  wellformed = False
    742                  self._log.error(u'Selector: Cannot end with combinator: %s' %
    743                      self._valuestr(selectorText))
    744
    745              if newseq and hasattr(newseq[-1].value, 'strip') and \
    746                 newseq[-1].value.strip() == u'':
    747                  del newseq[-1]
    748
    749              # set
    750              if wellformed:
    751                  self.__namespaces = namespaces
    752                  self._element = new['element']
    753                  self._specificity = tuple(new['specificity'])
    754                  self._setSeq(newseq)
    755                  # filter that only used ones are kept
    756                  self.__namespaces = self._getUsedNamespaces()
    757
    758      selectorText = property(_getSelectorText, _setSelectorText,
    759          doc="(DOM) The parsable textual representation of the selector.")
    760
    761
    762      specificity = property(lambda self: self._specificity,
    763                             doc="Specificity of this selector (READONLY).")
    764
    765      wellformed = property(lambda self: bool(len(self.seq)))
    766
    767 -    def __repr__(self):
    768          if self.__getNamespaces():
    769              st = (self.selectorText, self._getUsedNamespaces())
    770          else:
    771              st = self.selectorText
    772          return u"cssutils.css.%s(selectorText=%r)" % (
    773                  self.__class__.__name__, st)
    774
    775 -    def __str__(self):
    776          return u"<cssutils.css.%s object selectorText=%r specificity=%r _namespaces=%r at 0x%x>" % (
    777                  self.__class__.__name__, self.selectorText, self.specificity,
    778                  self._getUsedNamespaces(), id(self))
    779
    780 -    def _getUsedUris(self):
    781          "returns list of actually used URIs in this Selector"
    782          uris = set()
    783          for item in self.seq:
    784              type_, val = item.type, item.value
    785              if type_.endswith(u'-selector') or type_ == u'universal' and \
    786                 type(val) == tuple and val[0] not in (None, u'*'):
    787                  uris.add(val[0])
    788          return uris
    789
    790 -    def _getUsedNamespaces(self):
    791          "returns actually used namespaces only"
    792          useduris = self._getUsedUris()
    793          namespaces = _SimpleNamespaces()
    794          for p, uri in self._namespaces.items():
    795              if uri in useduris:
    796                  namespaces[p] = uri
    797          return namespaces
    798