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  __author__ = '$LastChangedBy: cthedot $'
     13  __date__ = '$LastChangedDate: 2008-02-22 19:03:20 +0100 (Fr, 22 Feb 2008) $'
     14  __version__ = '$LastChangedRevision: 1070 $'
     15
     16  import xml.dom
     17  import cssutils
     18  from cssutils.util import _SimpleNamespaces
     19
     20 -class Selector(cssutils.util.Base2):
     21      """
     22      (cssutils) a single selector in a SelectorList of a CSSStyleRule
     23
     24      Properties
     25      ==========
     26      element
     27          Effective element target of this selector
     28      parentList: of type SelectorList, readonly
     29          The SelectorList that contains this selector or None if this
     30          Selector is not attached to a SelectorList.
     31      selectorText
     32          textual representation of this Selector
     33      seq
     34          sequence of Selector parts including comments
     35      specificity (READONLY)
     36          tuple of (a, b, c, d) where:
     37
     38          a
     39              presence of style in document, always 0 if not used on a document
     40          b
     41              number of ID selectors
     42          c
     43              number of .class selectors
     44          d
     45              number of Element (type) selectors
     46
     47      wellformed
     48          if this selector is wellformed regarding the Selector spec
     49
     50      Format
     51      ======
     52      ::
     53
     54          # implemented in SelectorList
     55          selectors_group
     56            : selector [ COMMA S* selector ]*
     57            ;
     58
     59          selector
     60            : simple_selector_sequence [ combinator simple_selector_sequence ]*
     61            ;
     62
     63          combinator
     64            /* combinators can be surrounded by white space */
     65            : PLUS S* | GREATER S* | TILDE S* | S+
     66            ;
     67
     68          simple_selector_sequence
     69            : [ type_selector | universal ]
     70              [ HASH | class | attrib | pseudo | negation ]*
     71            | [ HASH | class | attrib | pseudo | negation ]+
     72            ;
     73
     74          type_selector
     75            : [ namespace_prefix ]? element_name
     76            ;
     77
     78          namespace_prefix
     79            : [ IDENT | '*' ]? '|'
     80            ;
     81
     82          element_name
     83            : IDENT
     84            ;
     85
     86          universal
     87            : [ namespace_prefix ]? '*'
     88            ;
     89
     90          class
     91            : '.' IDENT
     92            ;
     93
     94          attrib
     95            : '[' S* [ namespace_prefix ]? IDENT S*
     96                  [ [ PREFIXMATCH |
     97                      SUFFIXMATCH |
     98                      SUBSTRINGMATCH |
     99                      '=' |
    100                      INCLUDES |
    101                      DASHMATCH ] S* [ IDENT | STRING ] S*
    102                  ]? ']'
    103            ;
    104
    105          pseudo
    106            /* '::' starts a pseudo-element, ':' a pseudo-class */
    107            /* Exceptions: :first-line, :first-letter, :before and :after. */
    108            /* Note that pseudo-elements are restricted to one per selector and */
    109            /* occur only in the last simple_selector_sequence. */
    110            : ':' ':'? [ IDENT | functional_pseudo ]
    111            ;
    112
    113          functional_pseudo
    114            : FUNCTION S* expression ')'
    115            ;
    116
    117          expression
    118            /* In CSS3, the expressions are identifiers, strings, */
    119            /* or of the form "an+b" */
    120            : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
    121            ;
    122
    123          negation
    124            : NOT S* negation_arg S* ')'
    125            ;
    126
    127          negation_arg
    128            : type_selector | universal | HASH | class | attrib | pseudo
    129            ;
    130
    131      """
    132 -    def __init__(self, selectorText=None, parentList=None,
    133                   readonly=False):
    134          """
    135          :Parameters:
    136              selectorText
    137                  initial value of this selector
    138              parentList
    139                  a SelectorList
    140              readonly
    141                  default to False
    142          """
    143          super(Selector, self).__init__()
    144
    145          self.__namespaces = _SimpleNamespaces()
    146          self._element = None
    147          self._parent = parentList
    148          self._specificity = (0, 0, 0, 0)
    149
    150          if selectorText:
    151              self.selectorText = selectorText
    152
    153          self._readonly = readonly
    154
    155 -    def __getNamespaces(self):
    156          "uses own namespaces if not attached to a sheet, else the sheet's ones"
    157          try:
    158              return self._parent.parentRule.parentStyleSheet.namespaces
    159          except AttributeError:
    160              return self.__namespaces
    161
    162      _namespaces = property(__getNamespaces, doc="""if this Selector is attached
    163          to a CSSStyleSheet the namespaces of that sheet are mirrored here.
    164          While the Selector (or parent SelectorList or parentRule(s) of that are
    165          not attached a own dict of {prefix: namespaceURI} is used.""")
    166
    167
    168      element = property(lambda self: self._element,
    169                         doc=u"Effective element target of this selector.")
    170
    171      parentList = property(lambda self: self._parent,
    172          doc="(DOM) The SelectorList that contains this Selector or\
    173          None if this Selector is not attached to a SelectorList.")
    174
    175 -    def _getSelectorText(self):
    176          """
    177          returns serialized format
    178          """
    179          return cssutils.ser.do_css_Selector(self)
    180
    181 -    def _setSelectorText(self, selectorText):
    182          """
    183          :param selectorText:
    184              parsable string or a tuple of (selectorText, dict-of-namespaces).
    185              Given namespaces are ignored if this object is attached to a
    186              CSSStyleSheet!
    187
    188          :Exceptions:
    189              - `NAMESPACE_ERR`: (self)
    190                Raised if the specified selector uses an unknown namespace
    191                prefix.
    192              - `SYNTAX_ERR`: (self)
    193                Raised if the specified CSS string value has a syntax error
    194                and is unparsable.
    195              - `NO_MODIFICATION_ALLOWED_ERR`: (self)
    196                Raised if this rule is readonly.
    197          """
    198          self._checkReadonly()
    199
    200          # might be (selectorText, namespaces)
    201          selectorText, namespaces = self._splitNamespacesOff(selectorText)
    202          try:
    203              # uses parent stylesheets namespaces if available, otherwise given ones
    204              namespaces = self.parentList.parentRule.parentStyleSheet.namespaces
    205          except AttributeError:
    206              pass
    207          tokenizer = self._tokenize2(selectorText)
    208          if not tokenizer:
    209              self._log.error(u'Selector: No selectorText given.')
    210          else:
    211              # prepare tokenlist:
    212              #     "*" -> type "universal"
    213              #     "*"|IDENT + "|" -> combined to "namespace_prefix"
    214              #     "|" -> type "namespace_prefix"
    215              #     "." + IDENT -> combined to "class"
    216              #     ":" + IDENT, ":" + FUNCTION -> pseudo-class
    217              #     FUNCTION "not(" -> negation
    218              #     "::" + IDENT, "::" + FUNCTION -> pseudo-element
    219              tokens = []
    220              for t in tokenizer:
    221                  typ, val, lin, col = t
    222                  if val == u':' and tokens and\
    223                     self._tokenvalue(tokens[-1]) == ':':
    224                      # combine ":" and ":"
    225                      tokens[-1] = (typ, u'::', lin, col)
    226
    227                  elif typ == 'IDENT' and tokens\
    228                       and self._tokenvalue(tokens[-1]) == u'.':
    229                      # class: combine to .IDENT
    230                      tokens[-1] = ('class', u'.'+val, lin, col)
    231                  elif typ == 'IDENT' and tokens and \
    232                       self._tokenvalue(tokens[-1]).startswith(u':') and\
    233                       not self._tokenvalue(tokens[-1]).endswith(u'('):
    234                      # pseudo-X: combine to :IDENT or ::IDENT but not ":a(" + "b"
    235                      if self._tokenvalue(tokens[-1]).startswith(u'::'):
    236                          t = 'pseudo-element'
    237                      else:
    238                          t = 'pseudo-class'
    239                      tokens[-1] = (t, self._tokenvalue(tokens[-1])+val, lin, col)
    240
    241                  elif typ == 'FUNCTION' and val == u'not(' and tokens and \
    242                       u':' == self._tokenvalue(tokens[-1]):
    243                      tokens[-1] = ('negation', u':' + val, lin, tokens[-1][3])
    244                  elif typ == 'FUNCTION' and tokens\
    245                       and self._tokenvalue(tokens[-1]).startswith(u':'):
    246                      # pseudo-X: combine to :FUNCTION( or ::FUNCTION(
    247                      if self._tokenvalue(tokens[-1]).startswith(u'::'):
    248                          t = 'pseudo-element'
    249                      else:
    250                          t = 'pseudo-class'
    251                      tokens[-1] = (t, self._tokenvalue(tokens[-1])+val, lin, col)
    252
    253                  elif val == u'*' and tokens and\
    254                       self._type(tokens[-1]) == 'namespace_prefix' and\
    255                       self._tokenvalue(tokens[-1]).endswith(u'|'):
    256                      # combine prefix|*
    257                      tokens[-1] = ('universal', self._tokenvalue(tokens[-1])+val,
    258                                    lin, col)
    259                  elif val == u'*':
    260                      # universal: "*"
    261                      tokens.append(('universal', val, lin, col))
    262
    263                  elif val == u'|' and tokens and\
    264                       self._type(tokens[-1]) in (self._prods.IDENT, 'universal') and\
    265                       self._tokenvalue(tokens[-1]).find(u'|') == -1:
    266                      # namespace_prefix: "IDENT|" or "*|"
    267                      tokens[-1] = ('namespace_prefix',
    268                                    self._tokenvalue(tokens[-1])+u'|', lin, col)
    269                  elif val == u'|':
    270                      # namespace_prefix: "|"
    271                      tokens.append(('namespace_prefix', val, lin, col))
    272
    273                  else:
    274                      tokens.append(t)
    275
    276              # TODO: back to generator but not elegant at all!
    277              tokenizer = (t for t in tokens)
    278
    279              # for closures: must be a mutable
    280              new = {'context': [''], # stack of: 'attrib', 'negation', 'pseudo'
    281                     'element': None,
    282                     '_PREFIX': None,
    283                     'specificity': [0, 0, 0, 0], # mutable, finally a tuple!
    284                     'wellformed': True
    285                     }
    286              # used for equality checks and setting of a space combinator
    287              S = u' '
    288
    289              def append(seq, val, typ=None, token=None):
    290                  """
    291                  appends to seq
    292
    293                  namespace_prefix, IDENT will be combined to a tuple
    294                  (prefix, name) where prefix might be None, the empty string
    295                  or a prefix.
    296
    297                  Saved are also:
    298                      - specificity definition: style, id, class/att, type
    299                      - element: the element this Selector is for
    300                  """
    301                  context = new['context'][-1]
    302                  if token:
    303                      line, col = token[2], token[3]
    304                  else:
    305                      line, col = None, None
    306
    307                  if typ == '_PREFIX':
    308                      # SPECIAL TYPE: save prefix for combination with next
    309                      new['_PREFIX'] = val[:-1]
    310                      # handle next time
    311                      return
    312
    313                  if new['_PREFIX'] is not None:
    314                      # as saved from before and reset to None
    315                      prefix, new['_PREFIX'] = new['_PREFIX'], None
    316                  elif typ == 'universal' and '|' in val:
    317                      # val == *|* or prefix|*
    318                      prefix, val = val.split('|')
    319                  else:
    320                      prefix = None
    321
    322                  # namespace
    323                  if (typ.endswith('-selector') or typ == 'universal') and not (
    324                      'attribute-selector' == typ and not prefix):
    325                      # att **IS NOT** in default ns
    326                      if prefix == u'*':
    327                          # *|name: in ANY_NS
    328                          namespaceURI = cssutils._ANYNS
    329                      elif prefix is None:
    330                          # e or *: default namespace with prefix u'' or local-name()
    331                          namespaceURI = namespaces.get(u'', None)
    332                      elif prefix == u'':
    333                          # |name or |*: in no (or the empty) namespace
    334                          namespaceURI = u''
    335                      else:
    336                          # explicit namespace prefix
    337                          try:
    338                              namespaceURI = namespaces[prefix]
    339                          except KeyError:
    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
    682                  else:
    683                      new['wellformed'] = False
    684                      self._log.error(
    685                          u'Selector: Unexpected CHAR.', token=token)
    686                      return expected
    687
    688              def _negation(expected, seq, token, tokenizer=None):
    689                  # not(
    690                  context = new['context'][-1]
    691                  val = self._tokenvalue(token, normalize=True)
    692                  if 'negation' in expected:
    693                      new['context'].append('negation')
    694                      append(seq, val, 'negation-start', token=token)
    695                      return negation_arg
    696                  else:
    697                      new['wellformed'] = False
    698                      self._log.error(
    699                          u'Selector: Unexpected negation.', token=token)
    700                      return expected
    701
    702              # expected: only|not or mediatype, mediatype, feature, and
    703              newseq = self._tempSeq()
    704
    705              wellformed, expected = self._parse(expected=simple_selector_sequence,
    706                  seq=newseq, tokenizer=tokenizer,
    707                  productions={'CHAR': _char,
    708                               'class': _class,
    709                               'HASH': _hash,
    710                               'STRING': _string,
    711                               'IDENT': _ident,
    712                               'namespace_prefix': _namespace_prefix,
    713                               'negation': _negation,
    714                               'pseudo-class': _pseudo,
    715                               'pseudo-element': _pseudo,
    716                               'universal': _universal,
    717                               # pseudo
    718                               'NUMBER': _expression,
    719                               'DIMENSION': _expression,
    720                               # attribute
    721                               'PREFIXMATCH': _attcombinator,
    722                               'SUFFIXMATCH': _attcombinator,
    723                               'SUBSTRINGMATCH': _attcombinator,
    724                               'DASHMATCH': _attcombinator,
    725                               'INCLUDES': _attcombinator,
    726
    727                               'S': _S,
    728                               'COMMENT': _COMMENT})
    729              wellformed = wellformed and new['wellformed']
    730
    731              # post condition
    732              if len(new['context']) > 1:
    733                  wellformed = False
    734                  self._log.error(u'Selector: Incomplete selector: %s' %
    735                      self._valuestr(selectorText))
    736
    737              if expected == 'element_name':
    738                  wellformed = False
    739                  self._log.error(u'Selector: No element name found: %s' %
    740                      self._valuestr(selectorText))
    741
    742              if expected == simple_selector_sequence:
    743                  wellformed = False
    744                  self._log.error(u'Selector: Cannot end with combinator: %s' %
    745                      self._valuestr(selectorText))
    746
    747              if newseq and hasattr(newseq[-1].value, 'strip') and \
    748                 newseq[-1].value.strip() == u'':
    749                  del newseq[-1]
    750
    751              # set
    752              if wellformed:
    753                  self.__namespaces = namespaces
    754                  self._element = new['element']
    755                  self._specificity = tuple(new['specificity'])
    756                  self._setSeq(newseq)
    757                  # filter that only used ones are kept
    758                  self.__namespaces = self._getUsedNamespaces()
    759
    760      selectorText = property(_getSelectorText, _setSelectorText,
    761          doc="(DOM) The parsable textual representation of the selector.")
    762
    763
    764      specificity = property(lambda self: self._specificity,
    765                             doc="Specificity of this selector (READONLY).")
    766
    767      wellformed = property(lambda self: bool(len(self.seq)))
    768
    769 -    def __repr__(self):
    770          if self.__getNamespaces():
    771              st = (self.selectorText, self._getUsedNamespaces())
    772          else:
    773              st = self.selectorText
    774          return u"cssutils.css.%s(selectorText=%r)" % (
    775                  self.__class__.__name__, st)
    776
    777 -    def __str__(self):
    778          return u"<cssutils.css.%s object selectorText=%r specificity=%r _namespaces=%r at 0x%x>" % (
    779                  self.__class__.__name__, self.selectorText, self.specificity,
    780                  self._getUsedNamespaces(), id(self))
    781
    782 -    def _getUsedUris(self):
    783          "returns list of actually used URIs in this Selector"
    784          uris = set()
    785          for item in self.seq:
    786              type_, val = item.type, item.value
    787              if type_.endswith(u'-selector') or type_ == u'universal' and \
    788                 type(val) == tuple and val[0] not in (None, u'*'):
    789                  uris.add(val[0])
    790          return uris
    791
    792 -    def _getUsedNamespaces(self):
    793          "returns actually used namespaces only"
    794          useduris = self._getUsedUris()
    795          namespaces = _SimpleNamespaces()
    796          for p, uri in self._namespaces.items():
    797              if uri in useduris:
    798                  namespaces[p] = uri
    799          return namespaces
    800