Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* |
michael@0 | 2 | * Copyright (c) 2007 Henri Sivonen |
michael@0 | 3 | * Copyright (c) 2008-2011 Mozilla Foundation |
michael@0 | 4 | * |
michael@0 | 5 | * Permission is hereby granted, free of charge, to any person obtaining a |
michael@0 | 6 | * copy of this software and associated documentation files (the "Software"), |
michael@0 | 7 | * to deal in the Software without restriction, including without limitation |
michael@0 | 8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
michael@0 | 9 | * and/or sell copies of the Software, and to permit persons to whom the |
michael@0 | 10 | * Software is furnished to do so, subject to the following conditions: |
michael@0 | 11 | * |
michael@0 | 12 | * The above copyright notice and this permission notice shall be included in |
michael@0 | 13 | * all copies or substantial portions of the Software. |
michael@0 | 14 | * |
michael@0 | 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
michael@0 | 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
michael@0 | 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
michael@0 | 18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
michael@0 | 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
michael@0 | 20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
michael@0 | 21 | * DEALINGS IN THE SOFTWARE. |
michael@0 | 22 | */ |
michael@0 | 23 | |
michael@0 | 24 | package nu.validator.htmlparser.impl; |
michael@0 | 25 | |
michael@0 | 26 | import nu.validator.htmlparser.annotation.Auto; |
michael@0 | 27 | import nu.validator.htmlparser.annotation.IdType; |
michael@0 | 28 | import nu.validator.htmlparser.annotation.Local; |
michael@0 | 29 | import nu.validator.htmlparser.annotation.NsUri; |
michael@0 | 30 | import nu.validator.htmlparser.annotation.Prefix; |
michael@0 | 31 | import nu.validator.htmlparser.annotation.QName; |
michael@0 | 32 | import nu.validator.htmlparser.common.Interner; |
michael@0 | 33 | import nu.validator.htmlparser.common.XmlViolationPolicy; |
michael@0 | 34 | |
michael@0 | 35 | import org.xml.sax.Attributes; |
michael@0 | 36 | import org.xml.sax.SAXException; |
michael@0 | 37 | |
michael@0 | 38 | /** |
michael@0 | 39 | * Be careful with this class. QName is the name in from HTML tokenization. |
michael@0 | 40 | * Otherwise, please refer to the interface doc. |
michael@0 | 41 | * |
michael@0 | 42 | * @version $Id: AttributesImpl.java 206 2008-03-20 14:09:29Z hsivonen $ |
michael@0 | 43 | * @author hsivonen |
michael@0 | 44 | */ |
michael@0 | 45 | public final class HtmlAttributes implements Attributes { |
michael@0 | 46 | |
michael@0 | 47 | // [NOCPP[ |
michael@0 | 48 | |
michael@0 | 49 | private static final AttributeName[] EMPTY_ATTRIBUTENAMES = new AttributeName[0]; |
michael@0 | 50 | |
michael@0 | 51 | private static final String[] EMPTY_STRINGS = new String[0]; |
michael@0 | 52 | |
michael@0 | 53 | // ]NOCPP] |
michael@0 | 54 | |
michael@0 | 55 | public static final HtmlAttributes EMPTY_ATTRIBUTES = new HtmlAttributes( |
michael@0 | 56 | AttributeName.HTML); |
michael@0 | 57 | |
michael@0 | 58 | private int mode; |
michael@0 | 59 | |
michael@0 | 60 | private int length; |
michael@0 | 61 | |
michael@0 | 62 | private @Auto AttributeName[] names; |
michael@0 | 63 | |
michael@0 | 64 | private @Auto String[] values; // XXX perhaps make this @NoLength? |
michael@0 | 65 | |
michael@0 | 66 | // [NOCPP[ |
michael@0 | 67 | |
michael@0 | 68 | private String idValue; |
michael@0 | 69 | |
michael@0 | 70 | private int xmlnsLength; |
michael@0 | 71 | |
michael@0 | 72 | private AttributeName[] xmlnsNames; |
michael@0 | 73 | |
michael@0 | 74 | private String[] xmlnsValues; |
michael@0 | 75 | |
michael@0 | 76 | // ]NOCPP] |
michael@0 | 77 | |
michael@0 | 78 | public HtmlAttributes(int mode) { |
michael@0 | 79 | this.mode = mode; |
michael@0 | 80 | this.length = 0; |
michael@0 | 81 | /* |
michael@0 | 82 | * The length of 5 covers covers 98.3% of elements |
michael@0 | 83 | * according to Hixie |
michael@0 | 84 | */ |
michael@0 | 85 | this.names = new AttributeName[5]; |
michael@0 | 86 | this.values = new String[5]; |
michael@0 | 87 | |
michael@0 | 88 | // [NOCPP[ |
michael@0 | 89 | |
michael@0 | 90 | this.idValue = null; |
michael@0 | 91 | |
michael@0 | 92 | this.xmlnsLength = 0; |
michael@0 | 93 | |
michael@0 | 94 | this.xmlnsNames = HtmlAttributes.EMPTY_ATTRIBUTENAMES; |
michael@0 | 95 | |
michael@0 | 96 | this.xmlnsValues = HtmlAttributes.EMPTY_STRINGS; |
michael@0 | 97 | |
michael@0 | 98 | // ]NOCPP] |
michael@0 | 99 | } |
michael@0 | 100 | /* |
michael@0 | 101 | public HtmlAttributes(HtmlAttributes other) { |
michael@0 | 102 | this.mode = other.mode; |
michael@0 | 103 | this.length = other.length; |
michael@0 | 104 | this.names = new AttributeName[other.length]; |
michael@0 | 105 | this.values = new String[other.length]; |
michael@0 | 106 | // [NOCPP[ |
michael@0 | 107 | this.idValue = other.idValue; |
michael@0 | 108 | this.xmlnsLength = other.xmlnsLength; |
michael@0 | 109 | this.xmlnsNames = new AttributeName[other.xmlnsLength]; |
michael@0 | 110 | this.xmlnsValues = new String[other.xmlnsLength]; |
michael@0 | 111 | // ]NOCPP] |
michael@0 | 112 | } |
michael@0 | 113 | */ |
michael@0 | 114 | |
michael@0 | 115 | void destructor() { |
michael@0 | 116 | clear(0); |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | /** |
michael@0 | 120 | * Only use with a static argument |
michael@0 | 121 | * |
michael@0 | 122 | * @param name |
michael@0 | 123 | * @return |
michael@0 | 124 | */ |
michael@0 | 125 | public int getIndex(AttributeName name) { |
michael@0 | 126 | for (int i = 0; i < length; i++) { |
michael@0 | 127 | if (names[i] == name) { |
michael@0 | 128 | return i; |
michael@0 | 129 | } |
michael@0 | 130 | } |
michael@0 | 131 | return -1; |
michael@0 | 132 | } |
michael@0 | 133 | |
michael@0 | 134 | /** |
michael@0 | 135 | * Only use with static argument. |
michael@0 | 136 | * |
michael@0 | 137 | * @see org.xml.sax.Attributes#getValue(java.lang.String) |
michael@0 | 138 | */ |
michael@0 | 139 | public String getValue(AttributeName name) { |
michael@0 | 140 | int index = getIndex(name); |
michael@0 | 141 | if (index == -1) { |
michael@0 | 142 | return null; |
michael@0 | 143 | } else { |
michael@0 | 144 | return getValueNoBoundsCheck(index); |
michael@0 | 145 | } |
michael@0 | 146 | } |
michael@0 | 147 | |
michael@0 | 148 | public int getLength() { |
michael@0 | 149 | return length; |
michael@0 | 150 | } |
michael@0 | 151 | |
michael@0 | 152 | /** |
michael@0 | 153 | * Variant of <code>getLocalName(int index)</code> without bounds check. |
michael@0 | 154 | * @param index a valid attribute index |
michael@0 | 155 | * @return the local name at index |
michael@0 | 156 | */ |
michael@0 | 157 | public @Local String getLocalNameNoBoundsCheck(int index) { |
michael@0 | 158 | // CPPONLY: assert index < length && index >= 0: "Index out of bounds"; |
michael@0 | 159 | return names[index].getLocal(mode); |
michael@0 | 160 | } |
michael@0 | 161 | |
michael@0 | 162 | /** |
michael@0 | 163 | * Variant of <code>getURI(int index)</code> without bounds check. |
michael@0 | 164 | * @param index a valid attribute index |
michael@0 | 165 | * @return the namespace URI at index |
michael@0 | 166 | */ |
michael@0 | 167 | public @NsUri String getURINoBoundsCheck(int index) { |
michael@0 | 168 | // CPPONLY: assert index < length && index >= 0: "Index out of bounds"; |
michael@0 | 169 | return names[index].getUri(mode); |
michael@0 | 170 | } |
michael@0 | 171 | |
michael@0 | 172 | /** |
michael@0 | 173 | * Variant of <code>getPrefix(int index)</code> without bounds check. |
michael@0 | 174 | * @param index a valid attribute index |
michael@0 | 175 | * @return the namespace prefix at index |
michael@0 | 176 | */ |
michael@0 | 177 | public @Prefix String getPrefixNoBoundsCheck(int index) { |
michael@0 | 178 | // CPPONLY: assert index < length && index >= 0: "Index out of bounds"; |
michael@0 | 179 | return names[index].getPrefix(mode); |
michael@0 | 180 | } |
michael@0 | 181 | |
michael@0 | 182 | /** |
michael@0 | 183 | * Variant of <code>getValue(int index)</code> without bounds check. |
michael@0 | 184 | * @param index a valid attribute index |
michael@0 | 185 | * @return the attribute value at index |
michael@0 | 186 | */ |
michael@0 | 187 | public String getValueNoBoundsCheck(int index) { |
michael@0 | 188 | // CPPONLY: assert index < length && index >= 0: "Index out of bounds"; |
michael@0 | 189 | return values[index]; |
michael@0 | 190 | } |
michael@0 | 191 | |
michael@0 | 192 | /** |
michael@0 | 193 | * Variant of <code>getAttributeName(int index)</code> without bounds check. |
michael@0 | 194 | * @param index a valid attribute index |
michael@0 | 195 | * @return the attribute name at index |
michael@0 | 196 | */ |
michael@0 | 197 | public AttributeName getAttributeNameNoBoundsCheck(int index) { |
michael@0 | 198 | // CPPONLY: assert index < length && index >= 0: "Index out of bounds"; |
michael@0 | 199 | return names[index]; |
michael@0 | 200 | } |
michael@0 | 201 | |
michael@0 | 202 | // [NOCPP[ |
michael@0 | 203 | |
michael@0 | 204 | /** |
michael@0 | 205 | * Variant of <code>getQName(int index)</code> without bounds check. |
michael@0 | 206 | * @param index a valid attribute index |
michael@0 | 207 | * @return the QName at index |
michael@0 | 208 | */ |
michael@0 | 209 | public @QName String getQNameNoBoundsCheck(int index) { |
michael@0 | 210 | return names[index].getQName(mode); |
michael@0 | 211 | } |
michael@0 | 212 | |
michael@0 | 213 | /** |
michael@0 | 214 | * Variant of <code>getType(int index)</code> without bounds check. |
michael@0 | 215 | * @param index a valid attribute index |
michael@0 | 216 | * @return the attribute type at index |
michael@0 | 217 | */ |
michael@0 | 218 | public @IdType String getTypeNoBoundsCheck(int index) { |
michael@0 | 219 | return (names[index] == AttributeName.ID) ? "ID" : "CDATA"; |
michael@0 | 220 | } |
michael@0 | 221 | |
michael@0 | 222 | public int getIndex(String qName) { |
michael@0 | 223 | for (int i = 0; i < length; i++) { |
michael@0 | 224 | if (names[i].getQName(mode).equals(qName)) { |
michael@0 | 225 | return i; |
michael@0 | 226 | } |
michael@0 | 227 | } |
michael@0 | 228 | return -1; |
michael@0 | 229 | } |
michael@0 | 230 | |
michael@0 | 231 | public int getIndex(String uri, String localName) { |
michael@0 | 232 | for (int i = 0; i < length; i++) { |
michael@0 | 233 | if (names[i].getLocal(mode).equals(localName) |
michael@0 | 234 | && names[i].getUri(mode).equals(uri)) { |
michael@0 | 235 | return i; |
michael@0 | 236 | } |
michael@0 | 237 | } |
michael@0 | 238 | return -1; |
michael@0 | 239 | } |
michael@0 | 240 | |
michael@0 | 241 | public @IdType String getType(String qName) { |
michael@0 | 242 | int index = getIndex(qName); |
michael@0 | 243 | if (index == -1) { |
michael@0 | 244 | return null; |
michael@0 | 245 | } else { |
michael@0 | 246 | return getType(index); |
michael@0 | 247 | } |
michael@0 | 248 | } |
michael@0 | 249 | |
michael@0 | 250 | public @IdType String getType(String uri, String localName) { |
michael@0 | 251 | int index = getIndex(uri, localName); |
michael@0 | 252 | if (index == -1) { |
michael@0 | 253 | return null; |
michael@0 | 254 | } else { |
michael@0 | 255 | return getType(index); |
michael@0 | 256 | } |
michael@0 | 257 | } |
michael@0 | 258 | |
michael@0 | 259 | public String getValue(String qName) { |
michael@0 | 260 | int index = getIndex(qName); |
michael@0 | 261 | if (index == -1) { |
michael@0 | 262 | return null; |
michael@0 | 263 | } else { |
michael@0 | 264 | return getValue(index); |
michael@0 | 265 | } |
michael@0 | 266 | } |
michael@0 | 267 | |
michael@0 | 268 | public String getValue(String uri, String localName) { |
michael@0 | 269 | int index = getIndex(uri, localName); |
michael@0 | 270 | if (index == -1) { |
michael@0 | 271 | return null; |
michael@0 | 272 | } else { |
michael@0 | 273 | return getValue(index); |
michael@0 | 274 | } |
michael@0 | 275 | } |
michael@0 | 276 | |
michael@0 | 277 | public @Local String getLocalName(int index) { |
michael@0 | 278 | if (index < length && index >= 0) { |
michael@0 | 279 | return names[index].getLocal(mode); |
michael@0 | 280 | } else { |
michael@0 | 281 | return null; |
michael@0 | 282 | } |
michael@0 | 283 | } |
michael@0 | 284 | |
michael@0 | 285 | public @QName String getQName(int index) { |
michael@0 | 286 | if (index < length && index >= 0) { |
michael@0 | 287 | return names[index].getQName(mode); |
michael@0 | 288 | } else { |
michael@0 | 289 | return null; |
michael@0 | 290 | } |
michael@0 | 291 | } |
michael@0 | 292 | |
michael@0 | 293 | public @IdType String getType(int index) { |
michael@0 | 294 | if (index < length && index >= 0) { |
michael@0 | 295 | return (names[index] == AttributeName.ID) ? "ID" : "CDATA"; |
michael@0 | 296 | } else { |
michael@0 | 297 | return null; |
michael@0 | 298 | } |
michael@0 | 299 | } |
michael@0 | 300 | |
michael@0 | 301 | public AttributeName getAttributeName(int index) { |
michael@0 | 302 | if (index < length && index >= 0) { |
michael@0 | 303 | return names[index]; |
michael@0 | 304 | } else { |
michael@0 | 305 | return null; |
michael@0 | 306 | } |
michael@0 | 307 | } |
michael@0 | 308 | |
michael@0 | 309 | public @NsUri String getURI(int index) { |
michael@0 | 310 | if (index < length && index >= 0) { |
michael@0 | 311 | return names[index].getUri(mode); |
michael@0 | 312 | } else { |
michael@0 | 313 | return null; |
michael@0 | 314 | } |
michael@0 | 315 | } |
michael@0 | 316 | |
michael@0 | 317 | public @Prefix String getPrefix(int index) { |
michael@0 | 318 | if (index < length && index >= 0) { |
michael@0 | 319 | return names[index].getPrefix(mode); |
michael@0 | 320 | } else { |
michael@0 | 321 | return null; |
michael@0 | 322 | } |
michael@0 | 323 | } |
michael@0 | 324 | |
michael@0 | 325 | public String getValue(int index) { |
michael@0 | 326 | if (index < length && index >= 0) { |
michael@0 | 327 | return values[index]; |
michael@0 | 328 | } else { |
michael@0 | 329 | return null; |
michael@0 | 330 | } |
michael@0 | 331 | } |
michael@0 | 332 | |
michael@0 | 333 | public String getId() { |
michael@0 | 334 | return idValue; |
michael@0 | 335 | } |
michael@0 | 336 | |
michael@0 | 337 | public int getXmlnsLength() { |
michael@0 | 338 | return xmlnsLength; |
michael@0 | 339 | } |
michael@0 | 340 | |
michael@0 | 341 | public @Local String getXmlnsLocalName(int index) { |
michael@0 | 342 | if (index < xmlnsLength && index >= 0) { |
michael@0 | 343 | return xmlnsNames[index].getLocal(mode); |
michael@0 | 344 | } else { |
michael@0 | 345 | return null; |
michael@0 | 346 | } |
michael@0 | 347 | } |
michael@0 | 348 | |
michael@0 | 349 | public @NsUri String getXmlnsURI(int index) { |
michael@0 | 350 | if (index < xmlnsLength && index >= 0) { |
michael@0 | 351 | return xmlnsNames[index].getUri(mode); |
michael@0 | 352 | } else { |
michael@0 | 353 | return null; |
michael@0 | 354 | } |
michael@0 | 355 | } |
michael@0 | 356 | |
michael@0 | 357 | public String getXmlnsValue(int index) { |
michael@0 | 358 | if (index < xmlnsLength && index >= 0) { |
michael@0 | 359 | return xmlnsValues[index]; |
michael@0 | 360 | } else { |
michael@0 | 361 | return null; |
michael@0 | 362 | } |
michael@0 | 363 | } |
michael@0 | 364 | |
michael@0 | 365 | public int getXmlnsIndex(AttributeName name) { |
michael@0 | 366 | for (int i = 0; i < xmlnsLength; i++) { |
michael@0 | 367 | if (xmlnsNames[i] == name) { |
michael@0 | 368 | return i; |
michael@0 | 369 | } |
michael@0 | 370 | } |
michael@0 | 371 | return -1; |
michael@0 | 372 | } |
michael@0 | 373 | |
michael@0 | 374 | public String getXmlnsValue(AttributeName name) { |
michael@0 | 375 | int index = getXmlnsIndex(name); |
michael@0 | 376 | if (index == -1) { |
michael@0 | 377 | return null; |
michael@0 | 378 | } else { |
michael@0 | 379 | return getXmlnsValue(index); |
michael@0 | 380 | } |
michael@0 | 381 | } |
michael@0 | 382 | |
michael@0 | 383 | public AttributeName getXmlnsAttributeName(int index) { |
michael@0 | 384 | if (index < xmlnsLength && index >= 0) { |
michael@0 | 385 | return xmlnsNames[index]; |
michael@0 | 386 | } else { |
michael@0 | 387 | return null; |
michael@0 | 388 | } |
michael@0 | 389 | } |
michael@0 | 390 | |
michael@0 | 391 | // ]NOCPP] |
michael@0 | 392 | |
michael@0 | 393 | void addAttribute(AttributeName name, String value |
michael@0 | 394 | // [NOCPP[ |
michael@0 | 395 | , XmlViolationPolicy xmlnsPolicy |
michael@0 | 396 | // ]NOCPP] |
michael@0 | 397 | ) throws SAXException { |
michael@0 | 398 | // [NOCPP[ |
michael@0 | 399 | if (name == AttributeName.ID) { |
michael@0 | 400 | idValue = value; |
michael@0 | 401 | } |
michael@0 | 402 | |
michael@0 | 403 | if (name.isXmlns()) { |
michael@0 | 404 | if (xmlnsNames.length == xmlnsLength) { |
michael@0 | 405 | int newLen = xmlnsLength == 0 ? 2 : xmlnsLength << 1; |
michael@0 | 406 | AttributeName[] newNames = new AttributeName[newLen]; |
michael@0 | 407 | System.arraycopy(xmlnsNames, 0, newNames, 0, xmlnsNames.length); |
michael@0 | 408 | xmlnsNames = newNames; |
michael@0 | 409 | String[] newValues = new String[newLen]; |
michael@0 | 410 | System.arraycopy(xmlnsValues, 0, newValues, 0, xmlnsValues.length); |
michael@0 | 411 | xmlnsValues = newValues; |
michael@0 | 412 | } |
michael@0 | 413 | xmlnsNames[xmlnsLength] = name; |
michael@0 | 414 | xmlnsValues[xmlnsLength] = value; |
michael@0 | 415 | xmlnsLength++; |
michael@0 | 416 | switch (xmlnsPolicy) { |
michael@0 | 417 | case FATAL: |
michael@0 | 418 | // this is ugly |
michael@0 | 419 | throw new SAXException("Saw an xmlns attribute."); |
michael@0 | 420 | case ALTER_INFOSET: |
michael@0 | 421 | return; |
michael@0 | 422 | case ALLOW: |
michael@0 | 423 | // fall through |
michael@0 | 424 | } |
michael@0 | 425 | } |
michael@0 | 426 | |
michael@0 | 427 | // ]NOCPP] |
michael@0 | 428 | |
michael@0 | 429 | if (names.length == length) { |
michael@0 | 430 | int newLen = length << 1; // The first growth covers virtually |
michael@0 | 431 | // 100% of elements according to |
michael@0 | 432 | // Hixie |
michael@0 | 433 | AttributeName[] newNames = new AttributeName[newLen]; |
michael@0 | 434 | System.arraycopy(names, 0, newNames, 0, names.length); |
michael@0 | 435 | names = newNames; |
michael@0 | 436 | String[] newValues = new String[newLen]; |
michael@0 | 437 | System.arraycopy(values, 0, newValues, 0, values.length); |
michael@0 | 438 | values = newValues; |
michael@0 | 439 | } |
michael@0 | 440 | names[length] = name; |
michael@0 | 441 | values[length] = value; |
michael@0 | 442 | length++; |
michael@0 | 443 | } |
michael@0 | 444 | |
michael@0 | 445 | void clear(int m) { |
michael@0 | 446 | for (int i = 0; i < length; i++) { |
michael@0 | 447 | names[i].release(); |
michael@0 | 448 | names[i] = null; |
michael@0 | 449 | Portability.releaseString(values[i]); |
michael@0 | 450 | values[i] = null; |
michael@0 | 451 | } |
michael@0 | 452 | length = 0; |
michael@0 | 453 | mode = m; |
michael@0 | 454 | // [NOCPP[ |
michael@0 | 455 | idValue = null; |
michael@0 | 456 | for (int i = 0; i < xmlnsLength; i++) { |
michael@0 | 457 | xmlnsNames[i] = null; |
michael@0 | 458 | xmlnsValues[i] = null; |
michael@0 | 459 | } |
michael@0 | 460 | xmlnsLength = 0; |
michael@0 | 461 | // ]NOCPP] |
michael@0 | 462 | } |
michael@0 | 463 | |
michael@0 | 464 | /** |
michael@0 | 465 | * This is used in C++ to release special <code>isindex</code> |
michael@0 | 466 | * attribute values whose ownership is not transferred. |
michael@0 | 467 | */ |
michael@0 | 468 | void releaseValue(int i) { |
michael@0 | 469 | Portability.releaseString(values[i]); |
michael@0 | 470 | } |
michael@0 | 471 | |
michael@0 | 472 | /** |
michael@0 | 473 | * This is only used for <code>AttributeName</code> ownership transfer |
michael@0 | 474 | * in the isindex case to avoid freeing custom names twice in C++. |
michael@0 | 475 | */ |
michael@0 | 476 | void clearWithoutReleasingContents() { |
michael@0 | 477 | for (int i = 0; i < length; i++) { |
michael@0 | 478 | names[i] = null; |
michael@0 | 479 | values[i] = null; |
michael@0 | 480 | } |
michael@0 | 481 | length = 0; |
michael@0 | 482 | } |
michael@0 | 483 | |
michael@0 | 484 | boolean contains(AttributeName name) { |
michael@0 | 485 | for (int i = 0; i < length; i++) { |
michael@0 | 486 | if (name.equalsAnother(names[i])) { |
michael@0 | 487 | return true; |
michael@0 | 488 | } |
michael@0 | 489 | } |
michael@0 | 490 | // [NOCPP[ |
michael@0 | 491 | for (int i = 0; i < xmlnsLength; i++) { |
michael@0 | 492 | if (name.equalsAnother(xmlnsNames[i])) { |
michael@0 | 493 | return true; |
michael@0 | 494 | } |
michael@0 | 495 | } |
michael@0 | 496 | // ]NOCPP] |
michael@0 | 497 | return false; |
michael@0 | 498 | } |
michael@0 | 499 | |
michael@0 | 500 | public void adjustForMath() { |
michael@0 | 501 | mode = AttributeName.MATHML; |
michael@0 | 502 | } |
michael@0 | 503 | |
michael@0 | 504 | public void adjustForSvg() { |
michael@0 | 505 | mode = AttributeName.SVG; |
michael@0 | 506 | } |
michael@0 | 507 | |
michael@0 | 508 | public HtmlAttributes cloneAttributes(Interner interner) |
michael@0 | 509 | throws SAXException { |
michael@0 | 510 | assert (length == 0 |
michael@0 | 511 | // [NOCPP[ |
michael@0 | 512 | && xmlnsLength == 0 |
michael@0 | 513 | // ]NOCPP] |
michael@0 | 514 | ) |
michael@0 | 515 | || mode == 0 || mode == 3; |
michael@0 | 516 | HtmlAttributes clone = new HtmlAttributes(0); |
michael@0 | 517 | for (int i = 0; i < length; i++) { |
michael@0 | 518 | clone.addAttribute(names[i].cloneAttributeName(interner), |
michael@0 | 519 | Portability.newStringFromString(values[i]) |
michael@0 | 520 | // [NOCPP[ |
michael@0 | 521 | , XmlViolationPolicy.ALLOW |
michael@0 | 522 | // ]NOCPP] |
michael@0 | 523 | ); |
michael@0 | 524 | } |
michael@0 | 525 | // [NOCPP[ |
michael@0 | 526 | for (int i = 0; i < xmlnsLength; i++) { |
michael@0 | 527 | clone.addAttribute(xmlnsNames[i], xmlnsValues[i], |
michael@0 | 528 | XmlViolationPolicy.ALLOW); |
michael@0 | 529 | } |
michael@0 | 530 | // ]NOCPP] |
michael@0 | 531 | return clone; // XXX!!! |
michael@0 | 532 | } |
michael@0 | 533 | |
michael@0 | 534 | public boolean equalsAnother(HtmlAttributes other) { |
michael@0 | 535 | assert mode == 0 || mode == 3 : "Trying to compare attributes in foreign content."; |
michael@0 | 536 | int otherLength = other.getLength(); |
michael@0 | 537 | if (length != otherLength) { |
michael@0 | 538 | return false; |
michael@0 | 539 | } |
michael@0 | 540 | for (int i = 0; i < length; i++) { |
michael@0 | 541 | // Work around the limitations of C++ |
michael@0 | 542 | boolean found = false; |
michael@0 | 543 | // The comparing just the local names is OK, since these attribute |
michael@0 | 544 | // holders are both supposed to belong to HTML formatting elements |
michael@0 | 545 | @Local String ownLocal = names[i].getLocal(AttributeName.HTML); |
michael@0 | 546 | for (int j = 0; j < otherLength; j++) { |
michael@0 | 547 | if (ownLocal == other.names[j].getLocal(AttributeName.HTML)) { |
michael@0 | 548 | found = true; |
michael@0 | 549 | if (!Portability.stringEqualsString(values[i], other.values[j])) { |
michael@0 | 550 | return false; |
michael@0 | 551 | } |
michael@0 | 552 | } |
michael@0 | 553 | } |
michael@0 | 554 | if (!found) { |
michael@0 | 555 | return false; |
michael@0 | 556 | } |
michael@0 | 557 | } |
michael@0 | 558 | return true; |
michael@0 | 559 | } |
michael@0 | 560 | |
michael@0 | 561 | // [NOCPP[ |
michael@0 | 562 | |
michael@0 | 563 | void processNonNcNames(TreeBuilder<?> treeBuilder, XmlViolationPolicy namePolicy) throws SAXException { |
michael@0 | 564 | for (int i = 0; i < length; i++) { |
michael@0 | 565 | AttributeName attName = names[i]; |
michael@0 | 566 | if (!attName.isNcName(mode)) { |
michael@0 | 567 | String name = attName.getLocal(mode); |
michael@0 | 568 | switch (namePolicy) { |
michael@0 | 569 | case ALTER_INFOSET: |
michael@0 | 570 | names[i] = AttributeName.create(NCName.escapeName(name)); |
michael@0 | 571 | // fall through |
michael@0 | 572 | case ALLOW: |
michael@0 | 573 | if (attName != AttributeName.XML_LANG) { |
michael@0 | 574 | treeBuilder.warn("Attribute \u201C" + name + "\u201D is not serializable as XML 1.0."); |
michael@0 | 575 | } |
michael@0 | 576 | break; |
michael@0 | 577 | case FATAL: |
michael@0 | 578 | treeBuilder.fatal("Attribute \u201C" + name + "\u201D is not serializable as XML 1.0."); |
michael@0 | 579 | break; |
michael@0 | 580 | } |
michael@0 | 581 | } |
michael@0 | 582 | } |
michael@0 | 583 | } |
michael@0 | 584 | |
michael@0 | 585 | public void merge(HtmlAttributes attributes) throws SAXException { |
michael@0 | 586 | int len = attributes.getLength(); |
michael@0 | 587 | for (int i = 0; i < len; i++) { |
michael@0 | 588 | AttributeName name = attributes.getAttributeNameNoBoundsCheck(i); |
michael@0 | 589 | if (!contains(name)) { |
michael@0 | 590 | addAttribute(name, attributes.getValueNoBoundsCheck(i), XmlViolationPolicy.ALLOW); |
michael@0 | 591 | } |
michael@0 | 592 | } |
michael@0 | 593 | } |
michael@0 | 594 | |
michael@0 | 595 | |
michael@0 | 596 | // ]NOCPP] |
michael@0 | 597 | |
michael@0 | 598 | } |