parser/html/javasrc/HtmlAttributes.java

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

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 }

mercurial