| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/renderer/android/address_detector.h" | 5 #include "content/common/android/address_parser.h" |
| 6 | 6 |
| 7 #include <bitset> | 7 #include <bitset> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/string_util.h" | 10 #include "base/string_util.h" |
| 11 #include "base/utf_string_conversions.h" | 11 #include "base/utf_string_conversions.h" |
| 12 #include "net/base/escape.h" | 12 #include "net/base/escape.h" |
| 13 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" | |
| 14 | 13 |
| 15 namespace { | 14 namespace { |
| 16 | 15 |
| 17 // Prefix used for geographical address intent URIs. | |
| 18 static const char kAddressSchemaPrefix[] = "geo:0,0?q="; | |
| 19 | |
| 20 // Maximum text length to be searched for address detection. | |
| 21 static const size_t kMaxAddressLength = 250; | |
| 22 | |
| 23 // Minimum number of words in an address after the house number | 16 // Minimum number of words in an address after the house number |
| 24 // before a state is expected to be found. | 17 // before a state is expected to be found. |
| 25 // A value too high can miss short addresses. | 18 // A value too high can miss short addresses. |
| 26 const size_t kMinAddressWords = 3; | 19 const size_t kMinAddressWords = 3; |
| 27 | 20 |
| 28 // Maximum number of words allowed in an address between the house number | 21 // Maximum number of words allowed in an address between the house number |
| 29 // and the state, both not included. | 22 // and the state, both not included. |
| 30 const size_t kMaxAddressWords = 12; | 23 const size_t kMaxAddressWords = 12; |
| 31 | 24 |
| 32 // Maximum number of lines allowed in an address between the house number | 25 // Maximum number of lines allowed in an address between the house number |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 94 if (!*ascii_to_match || base::ToLowerASCII(*it) != *ascii_to_match) | 87 if (!*ascii_to_match || base::ToLowerASCII(*it) != *ascii_to_match) |
| 95 return false; | 88 return false; |
| 96 } | 89 } |
| 97 return *ascii_to_match == 0; | 90 return *ascii_to_match == 0; |
| 98 } | 91 } |
| 99 | 92 |
| 100 } // anonymous namespace | 93 } // anonymous namespace |
| 101 | 94 |
| 102 namespace content { | 95 namespace content { |
| 103 | 96 |
| 104 AddressDetector::AddressDetector() { | 97 AddressParser::AddressParser() { |
| 105 } | 98 } |
| 106 | 99 |
| 107 AddressDetector::~AddressDetector() { | 100 bool AddressParser::FindAddress(const string16& text, string16* address) { |
| 101 size_t start, end; |
| 102 if (FindContent(text.begin(), text.end(), &start, &end)) { |
| 103 address->assign(text.substr(start, end)); |
| 104 return true; |
| 105 } else { |
| 106 return false; |
| 107 } |
| 108 } | 108 } |
| 109 | 109 |
| 110 std::string AddressDetector::GetContentText(const string16& text) { | 110 bool AddressParser::FindContent(const string16::const_iterator& begin, |
| 111 // Get the address and replace unicode bullets with commas. | 111 const string16::const_iterator& end, |
| 112 string16 address_16 = CollapseWhitespace(text, false); | 112 size_t* start_pos, |
| 113 std::replace(address_16.begin(), address_16.end(), | 113 size_t* end_pos) { |
| 114 static_cast<char16>(0x2022), static_cast<char16>(',')); | |
| 115 return UTF16ToUTF8(address_16); | |
| 116 } | |
| 117 | |
| 118 GURL AddressDetector::GetIntentURL(const std::string& content_text) { | |
| 119 return GURL(kAddressSchemaPrefix + | |
| 120 net::EscapeQueryParamValue(content_text, true)); | |
| 121 } | |
| 122 | |
| 123 size_t AddressDetector::GetMaximumContentLength() { | |
| 124 return kMaxAddressLength; | |
| 125 } | |
| 126 | |
| 127 bool AddressDetector::FindContent(const string16::const_iterator& begin, | |
| 128 const string16::const_iterator& end, | |
| 129 size_t* start_pos, | |
| 130 size_t* end_pos, | |
| 131 std::string* content_text) { | |
| 132 HouseNumberParser house_number_parser; | 114 HouseNumberParser house_number_parser; |
| 133 | 115 |
| 134 // Keep going through the input string until a potential house number is | 116 // Keep going through the input string until a potential house number is |
| 135 // detected. Start tokenizing the following words to find a valid | 117 // detected. Start tokenizing the following words to find a valid |
| 136 // street name within a word range. Then, find a state name followed | 118 // street name within a word range. Then, find a state name followed |
| 137 // by a valid zip code for that state. Also keep a look for any other | 119 // by a valid zip code for that state. Also keep a look for any other |
| 138 // possible house numbers to continue from in case of no match and for | 120 // possible house numbers to continue from in case of no match and for |
| 139 // state names not followed by a zip code (e.g. New York, NY 10000). | 121 // state names not followed by a zip code (e.g. New York, NY 10000). |
| 140 const string16 newline_delimiters = kNewlineDelimiters; | 122 const string16 newline_delimiters = kNewlineDelimiters; |
| 141 const string16 delimiters = kWhitespaceUTF16 + newline_delimiters; | 123 const string16 delimiters = kWhitespaceUTF16 + newline_delimiters; |
| 142 for (string16::const_iterator it = begin; it != end; ) { | 124 for (string16::const_iterator it = begin; it != end; ) { |
| 143 Word house_number; | 125 Word house_number; |
| 144 if (!house_number_parser.Parse(it, end, &house_number)) | 126 if (!house_number_parser.Parse(it, end, &house_number)) |
| 145 return false; | 127 return false; |
| 146 | 128 |
| 147 String16Tokenizer tokenizer(house_number.end, end, delimiters); | 129 String16Tokenizer tokenizer(house_number.end, end, delimiters); |
| 148 tokenizer.set_options(String16Tokenizer::RETURN_DELIMS); | 130 tokenizer.set_options(String16Tokenizer::RETURN_DELIMS); |
| 149 | 131 |
| 150 std::vector<Word> words; | 132 WordList words; |
| 151 words.push_back(house_number); | 133 words.push_back(house_number); |
| 152 | 134 |
| 153 bool found_location_name = false; | 135 bool found_location_name = false; |
| 154 bool continue_on_house_number = true; | 136 bool continue_on_house_number = true; |
| 155 bool consecutive_house_numbers = true; | 137 bool consecutive_house_numbers = true; |
| 156 size_t next_house_number_word = 0; | 138 size_t next_house_number_word = 0; |
| 157 size_t num_lines = 1; | 139 size_t num_lines = 1; |
| 158 | 140 |
| 159 // Don't include the house number in the word count. | 141 // Don't include the house number in the word count. |
| 160 size_t next_word = 1; | 142 size_t next_word = 1; |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 256 tokenizer.token_end())); | 238 tokenizer.token_end())); |
| 257 } | 239 } |
| 258 | 240 |
| 259 // Check the parsing validity and state range of the zip code. | 241 // Check the parsing validity and state range of the zip code. |
| 260 next_word = state_last_word; | 242 next_word = state_last_word; |
| 261 if (!IsZipValid(words[zip_word], state_index)) | 243 if (!IsZipValid(words[zip_word], state_index)) |
| 262 continue; | 244 continue; |
| 263 | 245 |
| 264 *start_pos = words[0].begin - begin; | 246 *start_pos = words[0].begin - begin; |
| 265 *end_pos = words[zip_word].end - begin; | 247 *end_pos = words[zip_word].end - begin; |
| 266 content_text->assign(GetContentText(string16(words[0].begin, | |
| 267 words[zip_word].end))); | |
| 268 return true; | 248 return true; |
| 269 } | 249 } |
| 270 } | 250 } |
| 271 } | 251 } |
| 272 | 252 |
| 273 // Avoid skipping too many words because of a non-address number | 253 // Avoid skipping too many words because of a non-address number |
| 274 // at the beginning of the contents to parse. | 254 // at the beginning of the contents to parse. |
| 275 if (continue_on_house_number && next_house_number_word > 0) { | 255 if (continue_on_house_number && next_house_number_word > 0) { |
| 276 it = words[next_house_number_word].begin; | 256 it = words[next_house_number_word].begin; |
| 277 } else { | 257 } else { |
| 278 DCHECK(!words.empty()); | 258 DCHECK(!words.empty()); |
| 279 next_word = std::min(next_word, words.size() - 1); | 259 next_word = std::min(next_word, words.size() - 1); |
| 280 it = words[next_word].end; | 260 it = words[next_word].end; |
| 281 } | 261 } |
| 282 } | 262 } |
| 283 | 263 |
| 284 return false; | 264 return false; |
| 285 } | 265 } |
| 286 | 266 |
| 287 AddressDetector::Word::Word(const string16::const_iterator& begin, | 267 AddressParser::Word::Word(const string16::const_iterator& begin, |
| 288 const string16::const_iterator& end) | 268 const string16::const_iterator& end) |
| 289 : begin(begin), | 269 : begin(begin), |
| 290 end(end) { | 270 end(end) { |
| 291 DCHECK(begin <= end); | 271 DCHECK(begin <= end); |
| 292 } | 272 } |
| 293 | 273 |
| 294 bool AddressDetector::HouseNumberParser::IsPreDelimiter( | 274 bool AddressParser::HouseNumberParser::IsPreDelimiter(char16 character) { |
| 295 char16 character) { | |
| 296 return character == ':' || IsPostDelimiter(character); | 275 return character == ':' || IsPostDelimiter(character); |
| 297 } | 276 } |
| 298 | 277 |
| 299 bool AddressDetector::HouseNumberParser::IsPostDelimiter( | 278 bool AddressParser::HouseNumberParser::IsPostDelimiter(char16 character) { |
| 300 char16 character) { | |
| 301 return IsWhitespace(character) || strchr(",\"'", character); | 279 return IsWhitespace(character) || strchr(",\"'", character); |
| 302 } | 280 } |
| 303 | 281 |
| 304 void AddressDetector::HouseNumberParser::RestartOnNextDelimiter() { | 282 void AddressParser::HouseNumberParser::RestartOnNextDelimiter() { |
| 305 ResetState(); | 283 ResetState(); |
| 306 for (; it_ != end_ && !IsPreDelimiter(*it_); ++it_) {} | 284 for (; it_ != end_ && !IsPreDelimiter(*it_); ++it_) {} |
| 307 } | 285 } |
| 308 | 286 |
| 309 void AddressDetector::HouseNumberParser::AcceptChars(size_t num_chars) { | 287 void AddressParser::HouseNumberParser::AcceptChars(size_t num_chars) { |
| 310 size_t offset = std::min(static_cast<size_t>(std::distance(it_, end_)), | 288 size_t offset = std::min(static_cast<size_t>(std::distance(it_, end_)), |
| 311 num_chars); | 289 num_chars); |
| 312 it_ += offset; | 290 it_ += offset; |
| 313 result_chars_ += offset; | 291 result_chars_ += offset; |
| 314 } | 292 } |
| 315 | 293 |
| 316 void AddressDetector::HouseNumberParser::SkipChars(size_t num_chars) { | 294 void AddressParser::HouseNumberParser::SkipChars(size_t num_chars) { |
| 317 it_ += std::min(static_cast<size_t>(std::distance(it_, end_)), num_chars); | 295 it_ += std::min(static_cast<size_t>(std::distance(it_, end_)), num_chars); |
| 318 } | 296 } |
| 319 | 297 |
| 320 void AddressDetector::HouseNumberParser::ResetState() { | 298 void AddressParser::HouseNumberParser::ResetState() { |
| 321 num_digits_ = 0; | 299 num_digits_ = 0; |
| 322 result_chars_ = 0; | 300 result_chars_ = 0; |
| 323 } | 301 } |
| 324 | 302 |
| 325 bool AddressDetector::HouseNumberParser::CheckFinished(Word* word) const { | 303 bool AddressParser::HouseNumberParser::CheckFinished(Word* word) const { |
| 326 // There should always be a number after a hyphen. | 304 // There should always be a number after a hyphen. |
| 327 if (result_chars_ == 0 || SafePreviousChar(it_, begin_) == '-') | 305 if (result_chars_ == 0 || SafePreviousChar(it_, begin_) == '-') |
| 328 return false; | 306 return false; |
| 329 | 307 |
| 330 if (word) { | 308 if (word) { |
| 331 word->begin = it_ - result_chars_; | 309 word->begin = it_ - result_chars_; |
| 332 word->end = it_; | 310 word->end = it_; |
| 333 } | 311 } |
| 334 return true; | 312 return true; |
| 335 } | 313 } |
| 336 | 314 |
| 337 bool AddressDetector::HouseNumberParser::Parse( | 315 bool AddressParser::HouseNumberParser::Parse( |
| 338 const string16::const_iterator& begin, | 316 const string16::const_iterator& begin, |
| 339 const string16::const_iterator& end, Word* word) { | 317 const string16::const_iterator& end, Word* word) { |
| 340 it_ = begin_ = begin; | 318 it_ = begin_ = begin; |
| 341 end_ = end; | 319 end_ = end; |
| 342 ResetState(); | 320 ResetState(); |
| 343 | 321 |
| 344 // Iterations only used as a fail-safe against any buggy infinite loops. | 322 // Iterations only used as a fail-safe against any buggy infinite loops. |
| 345 size_t iterations = 0; | 323 size_t iterations = 0; |
| 346 size_t max_iterations = end - begin + 1; | 324 size_t max_iterations = end - begin + 1; |
| 347 for (; it_ != end_ && iterations < max_iterations; ++iterations) { | 325 for (; it_ != end_ && iterations < max_iterations; ++iterations) { |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 459 RestartOnNextDelimiter(); | 437 RestartOnNextDelimiter(); |
| 460 SkipChars(1); | 438 SkipChars(1); |
| 461 } | 439 } |
| 462 | 440 |
| 463 if (iterations >= max_iterations) | 441 if (iterations >= max_iterations) |
| 464 return false; | 442 return false; |
| 465 | 443 |
| 466 return CheckFinished(word); | 444 return CheckFinished(word); |
| 467 } | 445 } |
| 468 | 446 |
| 469 bool AddressDetector::FindStateStartingInWord( | 447 bool AddressParser::FindStateStartingInWord( |
| 470 WordList* words, | 448 WordList* words, |
| 471 size_t state_first_word, | 449 size_t state_first_word, |
| 472 size_t* state_last_word, | 450 size_t* state_last_word, |
| 473 String16Tokenizer* tokenizer, | 451 String16Tokenizer* tokenizer, |
| 474 size_t* state_index) { | 452 size_t* state_index) { |
| 475 | 453 |
| 476 // Bitmasks containing the allowed suffixes for 2-letter state codes. | 454 // Bitmasks containing the allowed suffixes for 2-letter state codes. |
| 477 static const int state_two_letter_suffix[23] = { | 455 static const int state_two_letter_suffix[23] = { |
| 478 0x02060c00, // A followed by: [KLRSZ]. | 456 0x02060c00, // A followed by: [KLRSZ]. |
| 479 0x00000000, // B. | 457 0x00000000, // B. |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 626 if (state_match) { | 604 if (state_match) { |
| 627 *state_last_word = state_word; | 605 *state_last_word = state_word; |
| 628 *state_index = state_names[state].state_index; | 606 *state_index = state_names[state].state_index; |
| 629 return true; | 607 return true; |
| 630 } | 608 } |
| 631 } | 609 } |
| 632 | 610 |
| 633 return false; | 611 return false; |
| 634 } | 612 } |
| 635 | 613 |
| 636 bool AddressDetector::IsZipValid(const Word& word, size_t state_index) { | 614 bool AddressParser::IsZipValid(const Word& word, size_t state_index) { |
| 637 size_t length = word.end - word.begin; | 615 size_t length = word.end - word.begin; |
| 638 if (length != kZipDigits && length != kZipPlus4Digits + 1) | 616 if (length != kZipDigits && length != kZipPlus4Digits + 1) |
| 639 return false; | 617 return false; |
| 640 | 618 |
| 641 for (string16::const_iterator it = word.begin; it != word.end; ++it) { | 619 for (string16::const_iterator it = word.begin; it != word.end; ++it) { |
| 642 size_t pos = it - word.begin; | 620 size_t pos = it - word.begin; |
| 643 if (IsAsciiDigit(*it) || (*it == '-' && pos == kZipDigits)) | 621 if (IsAsciiDigit(*it) || (*it == '-' && pos == kZipDigits)) |
| 644 continue; | 622 continue; |
| 645 return false; | 623 return false; |
| 646 } | 624 } |
| 647 return IsZipValidForState(word, state_index); | 625 return IsZipValidForState(word, state_index); |
| 648 } | 626 } |
| 649 | 627 |
| 650 bool AddressDetector::IsZipValidForState(const Word& word, size_t state_index) { | 628 bool AddressParser::IsZipValidForState(const Word& word, size_t state_index) { |
| 651 // List of valid zip code ranges. | 629 // List of valid zip code ranges. |
| 652 static const struct { | 630 static const struct { |
| 653 char low; | 631 char low; |
| 654 char high; | 632 char high; |
| 655 char exception1; | 633 char exception1; |
| 656 char exception2; | 634 char exception2; |
| 657 } zip_range[] = { | 635 } zip_range[] = { |
| 658 { 99, 99, -1, -1 }, // AK Alaska. | 636 { 99, 99, -1, -1 }, // AK Alaska. |
| 659 { 35, 36, -1, -1 }, // AL Alabama. | 637 { 35, 36, -1, -1 }, // AL Alabama. |
| 660 { 71, 72, -1, -1 }, // AR Arkansas. | 638 { 71, 72, -1, -1 }, // AR Arkansas. |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 724 | 702 |
| 725 if ((zip_prefix >= zip_range[state_index].low && | 703 if ((zip_prefix >= zip_range[state_index].low && |
| 726 zip_prefix <= zip_range[state_index].high) || | 704 zip_prefix <= zip_range[state_index].high) || |
| 727 zip_prefix == zip_range[state_index].exception1 || | 705 zip_prefix == zip_range[state_index].exception1 || |
| 728 zip_prefix == zip_range[state_index].exception2) { | 706 zip_prefix == zip_range[state_index].exception2) { |
| 729 return true; | 707 return true; |
| 730 } | 708 } |
| 731 return false; | 709 return false; |
| 732 } | 710 } |
| 733 | 711 |
| 734 bool AddressDetector::IsValidLocationName(const Word& word) { | 712 bool AddressParser::IsValidLocationName(const Word& word) { |
| 735 // Supported location names sorted alphabetically and grouped by first letter. | 713 // Supported location names sorted alphabetically and grouped by first letter. |
| 736 static const struct LocationNameInfo { | 714 static const struct LocationNameInfo { |
| 737 const char* string; | 715 const char* string; |
| 738 char length; | 716 char length; |
| 739 bool allow_plural; | 717 bool allow_plural; |
| 740 } location_names[157] = { | 718 } location_names[157] = { |
| 741 { "alley", 5, false }, { "annex", 5, false }, { "arcade", 6, false }, | 719 { "alley", 5, false }, { "annex", 5, false }, { "arcade", 6, false }, |
| 742 { "ave", 3, false }, { "ave.", 4, false }, { "avenue", 6, false }, | 720 { "ave", 3, false }, { "ave.", 4, false }, { "avenue", 6, false }, |
| 743 { "alameda", 7, false }, | 721 { "alameda", 7, false }, |
| 744 { "bayou", 5, false }, { "beach", 5, false }, { "bend", 4, false }, | 722 { "bayou", 5, false }, { "beach", 5, false }, { "bend", 4, false }, |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 836 location_names[i].string, | 814 location_names[i].string, |
| 837 location_names[i].allow_plural)) { | 815 location_names[i].allow_plural)) { |
| 838 return true; | 816 return true; |
| 839 } | 817 } |
| 840 } | 818 } |
| 841 | 819 |
| 842 return false; | 820 return false; |
| 843 } | 821 } |
| 844 | 822 |
| 845 } // namespace content | 823 } // namespace content |
| OLD | NEW |