1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/values.h" 6 7 #include "base/logging.h" 8 #include "base/string_util.h" 9 #include "base/utf_string_conversions.h" 10 11 namespace { 12 13 // Make a deep copy of |node|, but don't include empty lists or dictionaries 14 // in the copy. It's possible for this function to return NULL and it 15 // expects |node| to always be non-NULL. 16 Value* CopyWithoutEmptyChildren(Value* node) { 17 DCHECK(node); 18 switch (node->GetType()) { 19 case Value::TYPE_LIST: { 20 ListValue* list = static_cast<ListValue*>(node); 21 ListValue* copy = new ListValue; 22 for (ListValue::const_iterator it = list->begin(); it != list->end(); 23 ++it) { 24 Value* child_copy = CopyWithoutEmptyChildren(*it); 25 if (child_copy) 26 copy->Append(child_copy); 27 } 28 if (!copy->empty()) 29 return copy; 30 31 delete copy; 32 return NULL; 33 } 34 35 case Value::TYPE_DICTIONARY: { 36 DictionaryValue* dict = static_cast<DictionaryValue*>(node); 37 DictionaryValue* copy = new DictionaryValue; 38 for (DictionaryValue::key_iterator it = dict->begin_keys(); 39 it != dict->end_keys(); ++it) { 40 Value* child = NULL; 41 bool rv = dict->GetWithoutPathExpansion(*it, &child); 42 DCHECK(rv); 43 Value* child_copy = CopyWithoutEmptyChildren(child); 44 if (child_copy) 45 copy->SetWithoutPathExpansion(*it, child_copy); 46 } 47 if (!copy->empty()) 48 return copy; 49 50 delete copy; 51 return NULL; 52 } 53 54 default: 55 // For everything else, just make a copy. 56 return node->DeepCopy(); 57 } 58 } 59 60 } // namespace 61 62 ///////////////////// Value //////////////////// 63 64 Value::~Value() { 65 } 66 67 // static 68 Value* Value::CreateNullValue() { 69 return new Value(TYPE_NULL); 70 } 71 72 // static 73 Value* Value::CreateBooleanValue(bool in_value) { 74 return new FundamentalValue(in_value); 75 } 76 77 // static 78 Value* Value::CreateIntegerValue(int in_value) { 79 return new FundamentalValue(in_value); 80 } 81 82 // static 83 Value* Value::CreateRealValue(double in_value) { 84 return new FundamentalValue(in_value); 85 } 86 87 // static 88 Value* Value::CreateStringValue(const std::string& in_value) { 89 return new StringValue(in_value); 90 } 91 92 // static 93 Value* Value::CreateStringValue(const std::wstring& in_value) { 94 return new StringValue(in_value); 95 } 96 97 // static 98 Value* Value::CreateStringValueFromUTF16(const string16& in_value) { 99 return new StringValue(in_value); 100 } 101 102 // static 103 BinaryValue* Value::CreateBinaryValue(char* buffer, size_t size) { 104 return BinaryValue::Create(buffer, size); 105 } 106 107 bool Value::GetAsBoolean(bool* in_value) const { 108 return false; 109 } 110 111 bool Value::GetAsInteger(int* in_value) const { 112 return false; 113 } 114 115 bool Value::GetAsReal(double* in_value) const { 116 return false; 117 } 118 119 bool Value::GetAsString(std::string* in_value) const { 120 return false; 121 } 122 123 bool Value::GetAsString(std::wstring* in_value) const { 124 return false; 125 } 126 127 bool Value::GetAsUTF16(string16* out_value) const { 128 return false; 129 } 130 131 Value* Value::DeepCopy() const { 132 // This method should only be getting called for null Values--all subclasses 133 // need to provide their own implementation;. 134 DCHECK(IsType(TYPE_NULL)); 135 return CreateNullValue(); 136 } 137 138 bool Value::Equals(const Value* other) const { 139 // This method should only be getting called for null Values--all subclasses 140 // need to provide their own implementation;. 141 DCHECK(IsType(TYPE_NULL)); 142 return other->IsType(TYPE_NULL); 143 } 144 145 ///////////////////// FundamentalValue //////////////////// 146 147 FundamentalValue::~FundamentalValue() { 148 } 149 150 bool FundamentalValue::GetAsBoolean(bool* out_value) const { 151 if (out_value && IsType(TYPE_BOOLEAN)) 152 *out_value = boolean_value_; 153 return (IsType(TYPE_BOOLEAN)); 154 } 155 156 bool FundamentalValue::GetAsInteger(int* out_value) const { 157 if (out_value && IsType(TYPE_INTEGER)) 158 *out_value = integer_value_; 159 return (IsType(TYPE_INTEGER)); 160 } 161 162 bool FundamentalValue::GetAsReal(double* out_value) const { 163 if (out_value && IsType(TYPE_REAL)) 164 *out_value = real_value_; 165 return (IsType(TYPE_REAL)); 166 } 167 168 Value* FundamentalValue::DeepCopy() const { 169 switch (GetType()) { 170 case TYPE_BOOLEAN: 171 return CreateBooleanValue(boolean_value_); 172 173 case TYPE_INTEGER: 174 return CreateIntegerValue(integer_value_); 175 176 case TYPE_REAL: 177 return CreateRealValue(real_value_); 178 179 default: 180 NOTREACHED(); 181 return NULL; 182 } 183 } 184 185 bool FundamentalValue::Equals(const Value* other) const { 186 if (other->GetType() != GetType()) 187 return false; 188 189 switch (GetType()) { 190 case TYPE_BOOLEAN: { 191 bool lhs, rhs; 192 return GetAsBoolean(&lhs) && other->GetAsBoolean(&rhs) && lhs == rhs; 193 } 194 case TYPE_INTEGER: { 195 int lhs, rhs; 196 return GetAsInteger(&lhs) && other->GetAsInteger(&rhs) && lhs == rhs; 197 } 198 case TYPE_REAL: { 199 double lhs, rhs; 200 return GetAsReal(&lhs) && other->GetAsReal(&rhs) && lhs == rhs; 201 } 202 default: 203 NOTREACHED(); 204 return false; 205 } 206 } 207 208 ///////////////////// StringValue //////////////////// 209 210 StringValue::StringValue(const std::string& in_value) 211 : Value(TYPE_STRING), 212 value_(in_value) { 213 DCHECK(IsStringUTF8(in_value)); 214 } 215 216 StringValue::StringValue(const std::wstring& in_value) 217 : Value(TYPE_STRING), 218 value_(WideToUTF8(in_value)) { 219 } 220 221 #if !defined(WCHAR_T_IS_UTF16) 222 StringValue::StringValue(const string16& in_value) 223 : Value(TYPE_STRING), 224 value_(UTF16ToUTF8(in_value)) { 225 } 226 #endif 227 228 StringValue::~StringValue() { 229 } 230 231 bool StringValue::GetAsString(std::string* out_value) const { 232 if (out_value) 233 *out_value = value_; 234 return true; 235 } 236 237 bool StringValue::GetAsString(std::wstring* out_value) const { 238 if (out_value) 239 *out_value = UTF8ToWide(value_); 240 return true; 241 } 242 243 bool StringValue::GetAsUTF16(string16* out_value) const { 244 if (out_value) 245 *out_value = UTF8ToUTF16(value_); 246 return true; 247 } 248 249 Value* StringValue::DeepCopy() const { 250 return CreateStringValue(value_); 251 } 252 253 bool StringValue::Equals(const Value* other) const { 254 if (other->GetType() != GetType()) 255 return false; 256 std::string lhs, rhs; 257 return GetAsString(&lhs) && other->GetAsString(&rhs) && lhs == rhs; 258 } 259 260 ///////////////////// BinaryValue //////////////////// 261 262 // static 263 BinaryValue* BinaryValue::Create(char* buffer, size_t size) { 264 if (!buffer) 265 return NULL; 266 267 return new BinaryValue(buffer, size); 268 } 269 270 // static 271 BinaryValue* BinaryValue::CreateWithCopiedBuffer(const char* buffer, 272 size_t size) { 273 if (!buffer) 274 return NULL; 275 276 char* buffer_copy = new char[size]; 277 memcpy(buffer_copy, buffer, size); 278 return new BinaryValue(buffer_copy, size); 279 } 280 281 282 BinaryValue::BinaryValue(char* buffer, size_t size) 283 : Value(TYPE_BINARY), 284 buffer_(buffer), 285 size_(size) { 286 DCHECK(buffer_); 287 } 288 289 BinaryValue::~BinaryValue() { 290 DCHECK(buffer_); 291 if (buffer_) 292 delete[] buffer_; 293 } 294 295 Value* BinaryValue::DeepCopy() const { 296 return CreateWithCopiedBuffer(buffer_, size_); 297 } 298 299 bool BinaryValue::Equals(const Value* other) const { 300 if (other->GetType() != GetType()) 301 return false; 302 const BinaryValue* other_binary = static_cast<const BinaryValue*>(other); 303 if (other_binary->size_ != size_) 304 return false; 305 return !memcmp(buffer_, other_binary->buffer_, size_); 306 } 307 308 ///////////////////// DictionaryValue //////////////////// 309 310 DictionaryValue::~DictionaryValue() { 311 Clear(); 312 } 313 314 Value* DictionaryValue::DeepCopy() const { 315 DictionaryValue* result = new DictionaryValue; 316 317 for (ValueMap::const_iterator current_entry(dictionary_.begin()); 318 current_entry != dictionary_.end(); ++current_entry) { 319 result->SetWithoutPathExpansion(current_entry->first, 320 current_entry->second->DeepCopy()); 321 } 322 323 return result; 324 } 325 326 bool DictionaryValue::Equals(const Value* other) const { 327 if (other->GetType() != GetType()) 328 return false; 329 330 const DictionaryValue* other_dict = 331 static_cast<const DictionaryValue*>(other); 332 key_iterator lhs_it(begin_keys()); 333 key_iterator rhs_it(other_dict->begin_keys()); 334 while (lhs_it != end_keys() && rhs_it != other_dict->end_keys()) { 335 Value* lhs; 336 Value* rhs; 337 if (!GetWithoutPathExpansion(*lhs_it, &lhs) || 338 !other_dict->GetWithoutPathExpansion(*rhs_it, &rhs) || 339 !lhs->Equals(rhs)) { 340 return false; 341 } 342 ++lhs_it; 343 ++rhs_it; 344 } 345 if (lhs_it != end_keys() || rhs_it != other_dict->end_keys()) 346 return false; 347 348 return true; 349 } 350 351 bool DictionaryValue::HasKey(const std::wstring& key) const { 352 ValueMap::const_iterator current_entry = dictionary_.find(key); 353 DCHECK((current_entry == dictionary_.end()) || current_entry->second); 354 return current_entry != dictionary_.end(); 355 } 356 357 void DictionaryValue::Clear() { 358 ValueMap::iterator dict_iterator = dictionary_.begin(); 359 while (dict_iterator != dictionary_.end()) { 360 delete dict_iterator->second; 361 ++dict_iterator; 362 } 363 364 dictionary_.clear(); 365 } 366 367 void DictionaryValue::Set(const std::wstring& path, Value* in_value) { 368 DCHECK(in_value); 369 370 std::wstring current_path(path); 371 DictionaryValue* current_dictionary = this; 372 for (size_t delimiter_position = current_path.find('.'); 373 delimiter_position != std::wstring::npos; 374 delimiter_position = current_path.find('.')) { 375 // Assume that we're indexing into a dictionary. 376 std::wstring key(current_path, 0, delimiter_position); 377 DictionaryValue* child_dictionary = NULL; 378 if (!current_dictionary->GetDictionary(key, &child_dictionary)) { 379 child_dictionary = new DictionaryValue; 380 current_dictionary->SetWithoutPathExpansion(key, child_dictionary); 381 } 382 383 current_dictionary = child_dictionary; 384 current_path.erase(0, delimiter_position + 1); 385 } 386 387 current_dictionary->SetWithoutPathExpansion(current_path, in_value); 388 } 389 390 void DictionaryValue::SetBoolean(const std::wstring& path, bool in_value) { 391 Set(path, CreateBooleanValue(in_value)); 392 } 393 394 void DictionaryValue::SetInteger(const std::wstring& path, int in_value) { 395 Set(path, CreateIntegerValue(in_value)); 396 } 397 398 void DictionaryValue::SetReal(const std::wstring& path, double in_value) { 399 Set(path, CreateRealValue(in_value)); 400 } 401 402 void DictionaryValue::SetString(const std::wstring& path, 403 const std::string& in_value) { 404 Set(path, CreateStringValue(in_value)); 405 } 406 407 void DictionaryValue::SetString(const std::wstring& path, 408 const std::wstring& in_value) { 409 Set(path, CreateStringValue(in_value)); 410 } 411 412 void DictionaryValue::SetStringFromUTF16(const std::wstring& path, 413 const string16& in_value) { 414 Set(path, CreateStringValueFromUTF16(in_value)); 415 } 416 417 void DictionaryValue::SetWithoutPathExpansion(const std::wstring& key, 418 Value* in_value) { 419 // If there's an existing value here, we need to delete it, because 420 // we own all our children. 421 if (HasKey(key)) { 422 DCHECK(dictionary_[key] != in_value); // This would be bogus 423 delete dictionary_[key]; 424 } 425 426 dictionary_[key] = in_value; 427 } 428 429 bool DictionaryValue::Get(const std::wstring& path, Value** out_value) const { 430 std::wstring current_path(path); 431 const DictionaryValue* current_dictionary = this; 432 for (size_t delimiter_position = current_path.find('.'); 433 delimiter_position != std::wstring::npos; 434 delimiter_position = current_path.find('.')) { 435 DictionaryValue* child_dictionary = NULL; 436 if (!current_dictionary->GetDictionary( 437 current_path.substr(0, delimiter_position), &child_dictionary)) 438 return false; 439 440 current_dictionary = child_dictionary; 441 current_path.erase(0, delimiter_position + 1); 442 } 443 444 return current_dictionary->GetWithoutPathExpansion(current_path, out_value); 445 } 446 447 bool DictionaryValue::GetBoolean(const std::wstring& path, 448 bool* bool_value) const { 449 Value* value; 450 if (!Get(path, &value)) 451 return false; 452 453 return value->GetAsBoolean(bool_value); 454 } 455 456 bool DictionaryValue::GetInteger(const std::wstring& path, 457 int* out_value) const { 458 Value* value; 459 if (!Get(path, &value)) 460 return false; 461 462 return value->GetAsInteger(out_value); 463 } 464 465 bool DictionaryValue::GetReal(const std::wstring& path, 466 double* out_value) const { 467 Value* value; 468 if (!Get(path, &value)) 469 return false; 470 471 return value->GetAsReal(out_value); 472 } 473 474 bool DictionaryValue::GetString(const std::wstring& path, 475 std::string* out_value) const { 476 Value* value; 477 if (!Get(path, &value)) 478 return false; 479 480 return value->GetAsString(out_value); 481 } 482 483 bool DictionaryValue::GetString(const std::wstring& path, 484 std::wstring* out_value) const { 485 Value* value; 486 if (!Get(path, &value)) 487 return false; 488 489 return value->GetAsString(out_value); 490 } 491 492 bool DictionaryValue::GetStringAsUTF16(const std::wstring& path, 493 string16* out_value) const { 494 Value* value; 495 if (!Get(path, &value)) 496 return false; 497 498 return value->GetAsUTF16(out_value); 499 } 500 501 bool DictionaryValue::GetBinary(const std::wstring& path, 502 BinaryValue** out_value) const { 503 Value* value; 504 bool result = Get(path, &value); 505 if (!result || !value->IsType(TYPE_BINARY)) 506 return false; 507 508 if (out_value) 509 *out_value = static_cast<BinaryValue*>(value); 510 511 return true; 512 } 513 514 bool DictionaryValue::GetDictionary(const std::wstring& path, 515 DictionaryValue** out_value) const { 516 Value* value; 517 bool result = Get(path, &value); 518 if (!result || !value->IsType(TYPE_DICTIONARY)) 519 return false; 520 521 if (out_value) 522 *out_value = static_cast<DictionaryValue*>(value); 523 524 return true; 525 } 526 527 bool DictionaryValue::GetList(const std::wstring& path, 528 ListValue** out_value) const { 529 Value* value; 530 bool result = Get(path, &value); 531 if (!result || !value->IsType(TYPE_LIST)) 532 return false; 533 534 if (out_value) 535 *out_value = static_cast<ListValue*>(value); 536 537 return true; 538 } 539 540 bool DictionaryValue::GetWithoutPathExpansion(const std::wstring& key, 541 Value** out_value) const { 542 ValueMap::const_iterator entry_iterator = dictionary_.find(key); 543 if (entry_iterator == dictionary_.end()) 544 return false; 545 546 Value* entry = entry_iterator->second; 547 if (out_value) 548 *out_value = entry; 549 return true; 550 } 551 552 bool DictionaryValue::GetIntegerWithoutPathExpansion(const std::wstring& path, 553 int* out_value) const { 554 Value* value; 555 if (!GetWithoutPathExpansion(path, &value)) 556 return false; 557 558 return value->GetAsInteger(out_value); 559 } 560 561 bool DictionaryValue::GetStringWithoutPathExpansion( 562 const std::wstring& path, 563 std::string* out_value) const { 564 Value* value; 565 if (!GetWithoutPathExpansion(path, &value)) 566 return false; 567 568 return value->GetAsString(out_value); 569 } 570 571 bool DictionaryValue::GetStringWithoutPathExpansion( 572 const std::wstring& path, 573 std::wstring* out_value) const { 574 Value* value; 575 if (!GetWithoutPathExpansion(path, &value)) 576 return false; 577 578 return value->GetAsString(out_value); 579 } 580 581 bool DictionaryValue::GetStringAsUTF16WithoutPathExpansion( 582 const std::wstring& path, 583 string16* out_value) const { 584 Value* value; 585 if (!GetWithoutPathExpansion(path, &value)) 586 return false; 587 588 return value->GetAsUTF16(out_value); 589 } 590 591 bool DictionaryValue::GetDictionaryWithoutPathExpansion( 592 const std::wstring& path, 593 DictionaryValue** out_value) const { 594 Value* value; 595 bool result = GetWithoutPathExpansion(path, &value); 596 if (!result || !value->IsType(TYPE_DICTIONARY)) 597 return false; 598 599 if (out_value) 600 *out_value = static_cast<DictionaryValue*>(value); 601 602 return true; 603 } 604 605 bool DictionaryValue::GetListWithoutPathExpansion(const std::wstring& path, 606 ListValue** out_value) const { 607 Value* value; 608 bool result = GetWithoutPathExpansion(path, &value); 609 if (!result || !value->IsType(TYPE_LIST)) 610 return false; 611 612 if (out_value) 613 *out_value = static_cast<ListValue*>(value); 614 615 return true; 616 } 617 618 bool DictionaryValue::Remove(const std::wstring& path, Value** out_value) { 619 std::wstring current_path(path); 620 DictionaryValue* current_dictionary = this; 621 size_t delimiter_position = current_path.rfind('.'); 622 if (delimiter_position != std::wstring::npos) { 623 if (!GetDictionary(current_path.substr(0, delimiter_position), 624 ¤t_dictionary)) 625 return false; 626 current_path.erase(0, delimiter_position + 1); 627 } 628 629 return current_dictionary->RemoveWithoutPathExpansion(current_path, 630 out_value); 631 } 632 633 bool DictionaryValue::RemoveWithoutPathExpansion(const std::wstring& key, 634 Value** out_value) { 635 ValueMap::iterator entry_iterator = dictionary_.find(key); 636 if (entry_iterator == dictionary_.end()) 637 return false; 638 639 Value* entry = entry_iterator->second; 640 if (out_value) 641 *out_value = entry; 642 else 643 delete entry; 644 dictionary_.erase(entry_iterator); 645 return true; 646 } 647 648 DictionaryValue* DictionaryValue::DeepCopyWithoutEmptyChildren() { 649 Value* copy = CopyWithoutEmptyChildren(this); 650 return copy ? static_cast<DictionaryValue*>(copy) : new DictionaryValue; 651 } 652 653 ///////////////////// ListValue //////////////////// 654 655 ListValue::~ListValue() { 656 Clear(); 657 } 658 659 void ListValue::Clear() { 660 for (ValueVector::iterator i(list_.begin()); i != list_.end(); ++i) 661 delete *i; 662 list_.clear(); 663 } 664 665 bool ListValue::Set(size_t index, Value* in_value) { 666 if (!in_value) 667 return false; 668 669 if (index >= list_.size()) { 670 // Pad out any intermediate indexes with null settings 671 while (index > list_.size()) 672 Append(CreateNullValue()); 673 Append(in_value); 674 } else { 675 DCHECK(list_[index] != in_value); 676 delete list_[index]; 677 list_[index] = in_value; 678 } 679 return true; 680 } 681 682 bool ListValue::Get(size_t index, Value** out_value) const { 683 if (index >= list_.size()) 684 return false; 685 686 if (out_value) 687 *out_value = list_[index]; 688 689 return true; 690 } 691 692 bool ListValue::GetBoolean(size_t index, bool* bool_value) const { 693 Value* value; 694 if (!Get(index, &value)) 695 return false; 696 697 return value->GetAsBoolean(bool_value); 698 } 699 700 bool ListValue::GetInteger(size_t index, int* out_value) const { 701 Value* value; 702 if (!Get(index, &value)) 703 return false; 704 705 return value->GetAsInteger(out_value); 706 } 707 708 bool ListValue::GetReal(size_t index, double* out_value) const { 709 Value* value; 710 if (!Get(index, &value)) 711 return false; 712 713 return value->GetAsReal(out_value); 714 } 715 716 bool ListValue::GetString(size_t index, std::string* out_value) const { 717 Value* value; 718 if (!Get(index, &value)) 719 return false; 720 721 return value->GetAsString(out_value); 722 } 723 724 bool ListValue::GetString(size_t index, std::wstring* out_value) const { 725 Value* value; 726 if (!Get(index, &value)) 727 return false; 728 729 return value->GetAsString(out_value); 730 } 731 732 bool ListValue::GetStringAsUTF16(size_t index, string16* out_value) const { 733 Value* value; 734 if (!Get(index, &value)) 735 return false; 736 737 return value->GetAsUTF16(out_value); 738 } 739 740 bool ListValue::GetBinary(size_t index, BinaryValue** out_value) const { 741 Value* value; 742 bool result = Get(index, &value); 743 if (!result || !value->IsType(TYPE_BINARY)) 744 return false; 745 746 if (out_value) 747 *out_value = static_cast<BinaryValue*>(value); 748 749 return true; 750 } 751 752 bool ListValue::GetDictionary(size_t index, DictionaryValue** out_value) const { 753 Value* value; 754 bool result = Get(index, &value); 755 if (!result || !value->IsType(TYPE_DICTIONARY)) 756 return false; 757 758 if (out_value) 759 *out_value = static_cast<DictionaryValue*>(value); 760 761 return true; 762 } 763 764 bool ListValue::GetList(size_t index, ListValue** out_value) const { 765 Value* value; 766 bool result = Get(index, &value); 767 if (!result || !value->IsType(TYPE_LIST)) 768 return false; 769 770 if (out_value) 771 *out_value = static_cast<ListValue*>(value); 772 773 return true; 774 } 775 776 bool ListValue::Remove(size_t index, Value** out_value) { 777 if (index >= list_.size()) 778 return false; 779 780 if (out_value) 781 *out_value = list_[index]; 782 else 783 delete list_[index]; 784 785 list_.erase(list_.begin() + index); 786 return true; 787 } 788 789 int ListValue::Remove(const Value& value) { 790 for (ValueVector::iterator i(list_.begin()); i != list_.end(); ++i) { 791 if ((*i)->Equals(&value)) { 792 size_t index = i - list_.begin(); 793 delete *i; 794 list_.erase(i); 795 return index; 796 } 797 } 798 return -1; 799 } 800 801 void ListValue::Append(Value* in_value) { 802 DCHECK(in_value); 803 list_.push_back(in_value); 804 } 805 806 bool ListValue::Insert(size_t index, Value* in_value) { 807 DCHECK(in_value); 808 if (index > list_.size()) 809 return false; 810 811 list_.insert(list_.begin() + index, in_value); 812 return true; 813 } 814 815 Value* ListValue::DeepCopy() const { 816 ListValue* result = new ListValue; 817 818 for (ValueVector::const_iterator i(list_.begin()); i != list_.end(); ++i) 819 result->Append((*i)->DeepCopy()); 820 821 return result; 822 } 823 824 bool ListValue::Equals(const Value* other) const { 825 if (other->GetType() != GetType()) 826 return false; 827 828 const ListValue* other_list = 829 static_cast<const ListValue*>(other); 830 const_iterator lhs_it, rhs_it; 831 for (lhs_it = begin(), rhs_it = other_list->begin(); 832 lhs_it != end() && rhs_it != other_list->end(); 833 ++lhs_it, ++rhs_it) { 834 if (!(*lhs_it)->Equals(*rhs_it)) 835 return false; 836 } 837 if (lhs_it != end() || rhs_it != other_list->end()) 838 return false; 839 840 return true; 841 } 842