1"""CSSNamespaceRule currently implements http://dev.w3.org/csswg/css3-namespace/ 2""" 3__all__ = ['CSSNamespaceRule'] 4 5from . import cssrule 6import cssutils 7import xml.dom 8 9 10class CSSNamespaceRule(cssrule.CSSRule): 11 """ 12 Represents an @namespace rule within a CSS style sheet. 13 14 The @namespace at-rule declares a namespace prefix and associates 15 it with a given namespace (a string). This namespace prefix can then be 16 used in namespace-qualified names such as those described in the 17 Selectors Module [SELECT] or the Values and Units module [CSS3VAL]. 18 19 Dealing with these rules directly is not needed anymore, easier is 20 the use of :attr:`cssutils.css.CSSStyleSheet.namespaces`. 21 22 Format:: 23 24 namespace 25 : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S* 26 ; 27 namespace_prefix 28 : IDENT 29 ; 30 """ 31 32 def __init__( 33 self, 34 namespaceURI=None, 35 prefix=None, 36 cssText=None, 37 parentRule=None, 38 parentStyleSheet=None, 39 readonly=False, 40 ): 41 """ 42 :Parameters: 43 namespaceURI 44 The namespace URI (a simple string!) which is bound to the 45 given prefix. If no prefix is set 46 (``CSSNamespaceRule.prefix==''``) the namespace defined by 47 namespaceURI is set as the default namespace 48 prefix 49 The prefix used in the stylesheet for the given 50 ``CSSNamespaceRule.uri``. 51 cssText 52 if no namespaceURI is given cssText must be given to set 53 a namespaceURI as this is readonly later on 54 parentStyleSheet 55 sheet where this rule belongs to 56 57 Do not use as positional but as keyword parameters only! 58 59 If readonly allows setting of properties in constructor only 60 61 format namespace:: 62 63 namespace 64 : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S* 65 ; 66 namespace_prefix 67 : IDENT 68 ; 69 """ 70 super(CSSNamespaceRule, self).__init__( 71 parentRule=parentRule, parentStyleSheet=parentStyleSheet 72 ) 73 self._atkeyword = '@namespace' 74 self._prefix = '' 75 self._namespaceURI = None 76 77 if namespaceURI: 78 self.namespaceURI = namespaceURI 79 self.prefix = prefix 80 tempseq = self._tempSeq() 81 tempseq.append(self.prefix, 'prefix') 82 tempseq.append(self.namespaceURI, 'namespaceURI') 83 self._setSeq(tempseq) 84 85 elif cssText is not None: 86 self.cssText = cssText 87 88 if parentStyleSheet: 89 self._parentStyleSheet = parentStyleSheet 90 91 self._readonly = readonly 92 93 def __repr__(self): 94 return "cssutils.css.%s(namespaceURI=%r, prefix=%r)" % ( 95 self.__class__.__name__, 96 self.namespaceURI, 97 self.prefix, 98 ) 99 100 def __str__(self): 101 return "<cssutils.css.%s object namespaceURI=%r prefix=%r at 0x%x>" % ( 102 self.__class__.__name__, 103 self.namespaceURI, 104 self.prefix, 105 id(self), 106 ) 107 108 def _getCssText(self): 109 """Return serialized property cssText""" 110 return cssutils.ser.do_CSSNamespaceRule(self) 111 112 def _setCssText(self, cssText): # noqa: C901 113 """ 114 :param cssText: initial value for this rules cssText which is parsed 115 :exceptions: 116 - :exc:`~xml.dom.HierarchyRequestErr`: 117 Raised if the rule cannot be inserted at this point in the 118 style sheet. 119 - :exc:`~xml.dom.InvalidModificationErr`: 120 Raised if the specified CSS string value represents a different 121 type of rule than the current one. 122 - :exc:`~xml.dom.NoModificationAllowedErr`: 123 Raised if the rule is readonly. 124 - :exc:`~xml.dom.SyntaxErr`: 125 Raised if the specified CSS string value has a syntax error and 126 is unparsable. 127 """ 128 super(CSSNamespaceRule, self)._setCssText(cssText) 129 tokenizer = self._tokenize2(cssText) 130 attoken = self._nexttoken(tokenizer, None) 131 if self._type(attoken) != self._prods.NAMESPACE_SYM: 132 self._log.error( 133 'CSSNamespaceRule: No CSSNamespaceRule found: %s' 134 % self._valuestr(cssText), 135 error=xml.dom.InvalidModificationErr, 136 ) 137 else: 138 # for closures: must be a mutable 139 new = { 140 'keyword': self._tokenvalue(attoken), 141 'prefix': '', 142 'uri': None, 143 'wellformed': True, 144 } 145 146 def _ident(expected, seq, token, tokenizer=None): 147 # the namespace prefix, optional 148 if 'prefix or uri' == expected: 149 new['prefix'] = self._tokenvalue(token) 150 seq.append(new['prefix'], 'prefix') 151 return 'uri' 152 else: 153 new['wellformed'] = False 154 self._log.error('CSSNamespaceRule: Unexpected ident.', token) 155 return expected 156 157 def _string(expected, seq, token, tokenizer=None): 158 # the namespace URI as a STRING 159 if expected.endswith('uri'): 160 new['uri'] = self._stringtokenvalue(token) 161 seq.append(new['uri'], 'namespaceURI') 162 return ';' 163 164 else: 165 new['wellformed'] = False 166 self._log.error('CSSNamespaceRule: Unexpected string.', token) 167 return expected 168 169 def _uri(expected, seq, token, tokenizer=None): 170 # the namespace URI as URI which is DEPRECATED 171 if expected.endswith('uri'): 172 uri = self._uritokenvalue(token) 173 new['uri'] = uri 174 seq.append(new['uri'], 'namespaceURI') 175 return ';' 176 else: 177 new['wellformed'] = False 178 self._log.error('CSSNamespaceRule: Unexpected URI.', token) 179 return expected 180 181 def _char(expected, seq, token, tokenizer=None): 182 # final ; 183 val = self._tokenvalue(token) 184 if ';' == expected and ';' == val: 185 return 'EOF' 186 else: 187 new['wellformed'] = False 188 self._log.error('CSSNamespaceRule: Unexpected char.', token) 189 return expected 190 191 # "NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*" 192 newseq = self._tempSeq() 193 wellformed, expected = self._parse( 194 expected='prefix or uri', 195 seq=newseq, 196 tokenizer=tokenizer, 197 productions={ 198 'IDENT': _ident, 199 'STRING': _string, 200 'URI': _uri, 201 'CHAR': _char, 202 }, 203 new=new, 204 ) 205 206 # wellformed set by parse 207 wellformed = wellformed and new['wellformed'] 208 209 # post conditions 210 if new['uri'] is None: 211 wellformed = False 212 self._log.error( 213 'CSSNamespaceRule: No namespace URI found: %s' 214 % self._valuestr(cssText) 215 ) 216 217 if expected != 'EOF': 218 wellformed = False 219 self._log.error( 220 'CSSNamespaceRule: No ";" found: %s' % self._valuestr(cssText) 221 ) 222 223 # set all 224 if wellformed: 225 self.atkeyword = new['keyword'] 226 self._prefix = new['prefix'] 227 self.namespaceURI = new['uri'] 228 self._setSeq(newseq) 229 230 cssText = property( 231 fget=_getCssText, 232 fset=_setCssText, 233 doc="(DOM) The parsable textual representation of this " "rule.", 234 ) 235 236 def _setNamespaceURI(self, namespaceURI): 237 """ 238 :param namespaceURI: the initial value for this rules namespaceURI 239 :exceptions: 240 - :exc:`~xml.dom.NoModificationAllowedErr`: 241 (CSSRule) Raised if this rule is readonly or a namespaceURI is 242 already set in this rule. 243 """ 244 self._checkReadonly() 245 if not self._namespaceURI: 246 # initial setting 247 self._namespaceURI = namespaceURI 248 tempseq = self._tempSeq() 249 tempseq.append(namespaceURI, 'namespaceURI') 250 self._setSeq(tempseq) # makes seq readonly! 251 elif self._namespaceURI != namespaceURI: 252 self._log.error( 253 'CSSNamespaceRule: namespaceURI is readonly.', 254 error=xml.dom.NoModificationAllowedErr, 255 ) 256 257 namespaceURI = property( 258 lambda self: self._namespaceURI, 259 _setNamespaceURI, 260 doc="URI (handled as simple string) of the defined namespace.", 261 ) 262 263 def _replaceNamespaceURI(self, namespaceURI): 264 """Used during parse of new sheet only! 265 266 :param namespaceURI: the new value for this rules namespaceURI 267 """ 268 self._namespaceURI = namespaceURI 269 for i, x in enumerate(self._seq): 270 if 'namespaceURI' == x.type: 271 self._seq._readonly = False 272 self._seq.replace(i, namespaceURI, 'namespaceURI') 273 self._seq._readonly = True 274 break 275 276 def _setPrefix(self, prefix=None): 277 """ 278 :param prefix: the new prefix 279 :exceptions: 280 - :exc:`~xml.dom.SyntaxErr`: 281 Raised if the specified CSS string value has a syntax error and 282 is unparsable. 283 - :exc:`~xml.dom.NoModificationAllowedErr`: 284 Raised if this rule is readonly. 285 """ 286 self._checkReadonly() 287 if not prefix: 288 prefix = '' 289 else: 290 tokenizer = self._tokenize2(prefix) 291 prefixtoken = self._nexttoken(tokenizer, None) 292 if not prefixtoken or self._type(prefixtoken) != self._prods.IDENT: 293 self._log.error( 294 'CSSNamespaceRule: No valid prefix "%s".' % self._valuestr(prefix), 295 error=xml.dom.SyntaxErr, 296 ) 297 return 298 else: 299 prefix = self._tokenvalue(prefixtoken) 300 # update seq 301 for i, x in enumerate(self._seq): 302 if x == self._prefix: 303 self._seq[i] = (prefix, 'prefix', None, None) 304 break 305 else: 306 # put prefix at the beginning! 307 self._seq[0] = (prefix, 'prefix', None, None) 308 309 # set new prefix 310 self._prefix = prefix 311 312 prefix = property( 313 lambda self: self._prefix, 314 _setPrefix, 315 doc="Prefix used for the defined namespace.", 316 ) 317 318 type = property( 319 lambda self: self.NAMESPACE_RULE, 320 doc="The type of this rule, as defined by a CSSRule " "type constant.", 321 ) 322 323 wellformed = property(lambda self: self.namespaceURI is not None) 324