| Index: content/common/android/address_parser_internal.cc
|
| diff --git a/content/renderer/android/address_detector.cc b/content/common/android/address_parser_internal.cc
|
| similarity index 69%
|
| copy from content/renderer/android/address_detector.cc
|
| copy to content/common/android/address_parser_internal.cc
|
| index 891ac34d57ded46a0763585fe952b03a8c2130f8..46f9a0d9d7312e5c22926343b6883268c244ca0f 100644
|
| --- a/content/renderer/android/address_detector.cc
|
| +++ b/content/common/android/address_parser_internal.cc
|
| @@ -2,45 +2,15 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| -#include "content/renderer/android/address_detector.h"
|
| +#include "content/common/android/address_parser_internal.h"
|
|
|
| #include <bitset>
|
|
|
| #include "base/logging.h"
|
| #include "base/string_util.h"
|
| -#include "base/utf_string_conversions.h"
|
| -#include "net/base/escape.h"
|
| -#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
|
|
|
| namespace {
|
|
|
| -// Prefix used for geographical address intent URIs.
|
| -static const char kAddressSchemaPrefix[] = "geo:0,0?q=";
|
| -
|
| -// Maximum text length to be searched for address detection.
|
| -static const size_t kMaxAddressLength = 250;
|
| -
|
| -// Minimum number of words in an address after the house number
|
| -// before a state is expected to be found.
|
| -// A value too high can miss short addresses.
|
| -const size_t kMinAddressWords = 3;
|
| -
|
| -// Maximum number of words allowed in an address between the house number
|
| -// and the state, both not included.
|
| -const size_t kMaxAddressWords = 12;
|
| -
|
| -// Maximum number of lines allowed in an address between the house number
|
| -// and the state, both not included.
|
| -const size_t kMaxAddressLines = 5;
|
| -
|
| -// Maximum length allowed for any address word between the house number
|
| -// and the state, both not included.
|
| -const size_t kMaxAddressNameWordLength = 25;
|
| -
|
| -// Maximum number of words after the house number in which the location name
|
| -// should be found.
|
| -const size_t kMaxLocationNameDistance = 4;
|
| -
|
| // Number of digits for a valid zip code.
|
| const size_t kZipDigits = 5;
|
|
|
| @@ -50,15 +20,6 @@ const size_t kZipPlus4Digits = 9;
|
| // Maximum number of digits of a house number, including possible hyphens.
|
| const size_t kMaxHouseDigits = 5;
|
|
|
| -// Additional characters used as new line delimiters.
|
| -const char16 kNewlineDelimiters[] = {
|
| - '\n',
|
| - ',',
|
| - '*',
|
| - 0x2022, // Unicode bullet
|
| - 0,
|
| -};
|
| -
|
| char16 SafePreviousChar(const string16::const_iterator& it,
|
| const string16::const_iterator& begin) {
|
| if (it == begin)
|
| @@ -101,228 +62,47 @@ bool LowerCaseEqualsASCIIWithPlural(string16::const_iterator word_begin,
|
|
|
| namespace content {
|
|
|
| -AddressDetector::AddressDetector() {
|
| -}
|
| -
|
| -AddressDetector::~AddressDetector() {
|
| -}
|
| -
|
| -std::string AddressDetector::GetContentText(const string16& text) {
|
| - // Get the address and replace unicode bullets with commas.
|
| - string16 address_16 = CollapseWhitespace(text, false);
|
| - std::replace(address_16.begin(), address_16.end(),
|
| - static_cast<char16>(0x2022), static_cast<char16>(','));
|
| - return UTF16ToUTF8(address_16);
|
| -}
|
| -
|
| -GURL AddressDetector::GetIntentURL(const std::string& content_text) {
|
| - return GURL(kAddressSchemaPrefix +
|
| - net::EscapeQueryParamValue(content_text, true));
|
| -}
|
| -
|
| -size_t AddressDetector::GetMaximumContentLength() {
|
| - return kMaxAddressLength;
|
| -}
|
| -
|
| -bool AddressDetector::FindContent(const string16::const_iterator& begin,
|
| - const string16::const_iterator& end,
|
| - size_t* start_pos,
|
| - size_t* end_pos,
|
| - std::string* content_text) {
|
| - HouseNumberParser house_number_parser;
|
| -
|
| - // Keep going through the input string until a potential house number is
|
| - // detected. Start tokenizing the following words to find a valid
|
| - // street name within a word range. Then, find a state name followed
|
| - // by a valid zip code for that state. Also keep a look for any other
|
| - // possible house numbers to continue from in case of no match and for
|
| - // state names not followed by a zip code (e.g. New York, NY 10000).
|
| - const string16 newline_delimiters = kNewlineDelimiters;
|
| - const string16 delimiters = kWhitespaceUTF16 + newline_delimiters;
|
| - for (string16::const_iterator it = begin; it != end; ) {
|
| - Word house_number;
|
| - if (!house_number_parser.Parse(it, end, &house_number))
|
| - return false;
|
| -
|
| - String16Tokenizer tokenizer(house_number.end, end, delimiters);
|
| - tokenizer.set_options(String16Tokenizer::RETURN_DELIMS);
|
| -
|
| - std::vector<Word> words;
|
| - words.push_back(house_number);
|
| -
|
| - bool found_location_name = false;
|
| - bool continue_on_house_number = true;
|
| - bool consecutive_house_numbers = true;
|
| - size_t next_house_number_word = 0;
|
| - size_t num_lines = 1;
|
| -
|
| - // Don't include the house number in the word count.
|
| - size_t next_word = 1;
|
| - for (; next_word <= kMaxAddressWords + 1; ++next_word) {
|
| -
|
| - // Extract a new word from the tokenizer.
|
| - if (next_word == words.size()) {
|
| - do {
|
| - if (!tokenizer.GetNext())
|
| - return false;
|
| -
|
| - // Check the number of address lines.
|
| - if (tokenizer.token_is_delim() && newline_delimiters.find(
|
| - *tokenizer.token_begin()) != string16::npos) {
|
| - ++num_lines;
|
| - }
|
| - } while (tokenizer.token_is_delim());
|
| -
|
| - if (num_lines > kMaxAddressLines)
|
| - break;
|
| -
|
| - words.push_back(Word(tokenizer.token_begin(), tokenizer.token_end()));
|
| - }
|
| -
|
| - // Check the word length. If too long, don't try to continue from
|
| - // the next house number as no address can hold this word.
|
| - const Word& current_word = words[next_word];
|
| - DCHECK_GT(std::distance(current_word.begin, current_word.end), 0);
|
| - size_t current_word_length = std::distance(
|
| - current_word.begin, current_word.end);
|
| - if (current_word_length > kMaxAddressNameWordLength) {
|
| - continue_on_house_number = false;
|
| - break;
|
| - }
|
| -
|
| - // Check if the new word is a valid house number.
|
| - if (house_number_parser.Parse(current_word.begin, current_word.end,
|
| - NULL)) {
|
| - // Increase the number of consecutive house numbers since the beginning.
|
| - if (consecutive_house_numbers) {
|
| - // Check if there is a new line between consecutive house numbers.
|
| - // This avoids false positives of the form "Cafe 21\n 750 Fifth Ave.."
|
| - if (num_lines > 1) {
|
| - next_house_number_word = next_word;
|
| - break;
|
| - }
|
| - }
|
| -
|
| - // Keep the next candidate to resume parsing from in case of failure.
|
| - if (next_house_number_word == 0) {
|
| - next_house_number_word = next_word;
|
| - continue;
|
| - }
|
| - } else {
|
| - consecutive_house_numbers = false;
|
| - }
|
| -
|
| - // Look for location names in the words after the house number.
|
| - // A range limitation is introduced to avoid matching
|
| - // anything that starts with a number before a legitimate address.
|
| - if (next_word <= kMaxLocationNameDistance &&
|
| - IsValidLocationName(current_word)) {
|
| - found_location_name = true;
|
| - continue;
|
| - }
|
| -
|
| - // Don't count the house number.
|
| - if (next_word > kMinAddressWords) {
|
| - // Looking for the state is likely to add new words to the list while
|
| - // checking for multi-word state names.
|
| - size_t state_first_word = next_word;
|
| - size_t state_last_word, state_index;
|
| - if (FindStateStartingInWord(&words, state_first_word, &state_last_word,
|
| - &tokenizer, &state_index)) {
|
| -
|
| - // A location name should have been found at this point.
|
| - if (!found_location_name)
|
| - break;
|
| -
|
| - // Explicitly exclude "et al", as "al" is a valid state code.
|
| - if (current_word_length == 2 && words.size() > 2) {
|
| - const Word& previous_word = words[state_first_word - 1];
|
| - if (previous_word.end - previous_word.begin == 2 &&
|
| - LowerCaseEqualsASCII(previous_word.begin, previous_word.end,
|
| - "et") &&
|
| - LowerCaseEqualsASCII(current_word.begin, current_word.end,
|
| - "al"))
|
| - break;
|
| - }
|
| -
|
| - // Extract one more word from the tokenizer if not already available.
|
| - size_t zip_word = state_last_word + 1;
|
| - if (zip_word == words.size()) {
|
| - do {
|
| - if (!tokenizer.GetNext())
|
| - return false;
|
| - } while (tokenizer.token_is_delim());
|
| - words.push_back(Word(tokenizer.token_begin(),
|
| - tokenizer.token_end()));
|
| - }
|
| -
|
| - // Check the parsing validity and state range of the zip code.
|
| - next_word = state_last_word;
|
| - if (!IsZipValid(words[zip_word], state_index))
|
| - continue;
|
| -
|
| - *start_pos = words[0].begin - begin;
|
| - *end_pos = words[zip_word].end - begin;
|
| - content_text->assign(GetContentText(string16(words[0].begin,
|
| - words[zip_word].end)));
|
| - return true;
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Avoid skipping too many words because of a non-address number
|
| - // at the beginning of the contents to parse.
|
| - if (continue_on_house_number && next_house_number_word > 0) {
|
| - it = words[next_house_number_word].begin;
|
| - } else {
|
| - DCHECK(!words.empty());
|
| - next_word = std::min(next_word, words.size() - 1);
|
| - it = words[next_word].end;
|
| - }
|
| - }
|
| +namespace address_parser {
|
|
|
| - return false;
|
| -}
|
| +namespace internal {
|
|
|
| -AddressDetector::Word::Word(const string16::const_iterator& begin,
|
| - const string16::const_iterator& end)
|
| +Word::Word(const string16::const_iterator& begin,
|
| + const string16::const_iterator& end)
|
| : begin(begin),
|
| end(end) {
|
| DCHECK(begin <= end);
|
| }
|
|
|
| -bool AddressDetector::HouseNumberParser::IsPreDelimiter(
|
| - char16 character) {
|
| +bool HouseNumberParser::IsPreDelimiter(char16 character) {
|
| return character == ':' || IsPostDelimiter(character);
|
| }
|
|
|
| -bool AddressDetector::HouseNumberParser::IsPostDelimiter(
|
| - char16 character) {
|
| +bool HouseNumberParser::IsPostDelimiter(char16 character) {
|
| return IsWhitespace(character) || strchr(",\"'", character);
|
| }
|
|
|
| -void AddressDetector::HouseNumberParser::RestartOnNextDelimiter() {
|
| +void HouseNumberParser::RestartOnNextDelimiter() {
|
| ResetState();
|
| for (; it_ != end_ && !IsPreDelimiter(*it_); ++it_) {}
|
| }
|
|
|
| -void AddressDetector::HouseNumberParser::AcceptChars(size_t num_chars) {
|
| +void HouseNumberParser::AcceptChars(size_t num_chars) {
|
| size_t offset = std::min(static_cast<size_t>(std::distance(it_, end_)),
|
| num_chars);
|
| it_ += offset;
|
| result_chars_ += offset;
|
| }
|
|
|
| -void AddressDetector::HouseNumberParser::SkipChars(size_t num_chars) {
|
| +void HouseNumberParser::SkipChars(size_t num_chars) {
|
| it_ += std::min(static_cast<size_t>(std::distance(it_, end_)), num_chars);
|
| }
|
|
|
| -void AddressDetector::HouseNumberParser::ResetState() {
|
| +void HouseNumberParser::ResetState() {
|
| num_digits_ = 0;
|
| result_chars_ = 0;
|
| }
|
|
|
| -bool AddressDetector::HouseNumberParser::CheckFinished(Word* word) const {
|
| +bool HouseNumberParser::CheckFinished(Word* word) const {
|
| // There should always be a number after a hyphen.
|
| if (result_chars_ == 0 || SafePreviousChar(it_, begin_) == '-')
|
| return false;
|
| @@ -334,7 +114,7 @@ bool AddressDetector::HouseNumberParser::CheckFinished(Word* word) const {
|
| return true;
|
| }
|
|
|
| -bool AddressDetector::HouseNumberParser::Parse(
|
| +bool HouseNumberParser::Parse(
|
| const string16::const_iterator& begin,
|
| const string16::const_iterator& end, Word* word) {
|
| it_ = begin_ = begin;
|
| @@ -466,12 +246,11 @@ bool AddressDetector::HouseNumberParser::Parse(
|
| return CheckFinished(word);
|
| }
|
|
|
| -bool AddressDetector::FindStateStartingInWord(
|
| - WordList* words,
|
| - size_t state_first_word,
|
| - size_t* state_last_word,
|
| - String16Tokenizer* tokenizer,
|
| - size_t* state_index) {
|
| +bool FindStateStartingInWord(WordList* words,
|
| + size_t state_first_word,
|
| + size_t* state_last_word,
|
| + String16Tokenizer* tokenizer,
|
| + size_t* state_index) {
|
|
|
| // Bitmasks containing the allowed suffixes for 2-letter state codes.
|
| static const int state_two_letter_suffix[23] = {
|
| @@ -633,7 +412,7 @@ bool AddressDetector::FindStateStartingInWord(
|
| return false;
|
| }
|
|
|
| -bool AddressDetector::IsZipValid(const Word& word, size_t state_index) {
|
| +bool IsZipValid(const Word& word, size_t state_index) {
|
| size_t length = word.end - word.begin;
|
| if (length != kZipDigits && length != kZipPlus4Digits + 1)
|
| return false;
|
| @@ -647,7 +426,7 @@ bool AddressDetector::IsZipValid(const Word& word, size_t state_index) {
|
| return IsZipValidForState(word, state_index);
|
| }
|
|
|
| -bool AddressDetector::IsZipValidForState(const Word& word, size_t state_index) {
|
| +bool IsZipValidForState(const Word& word, size_t state_index) {
|
| // List of valid zip code ranges.
|
| static const struct {
|
| char low;
|
| @@ -731,7 +510,7 @@ bool AddressDetector::IsZipValidForState(const Word& word, size_t state_index) {
|
| return false;
|
| }
|
|
|
| -bool AddressDetector::IsValidLocationName(const Word& word) {
|
| +bool IsValidLocationName(const Word& word) {
|
| // Supported location names sorted alphabetically and grouped by first letter.
|
| static const struct LocationNameInfo {
|
| const char* string;
|
| @@ -842,4 +621,8 @@ bool AddressDetector::IsValidLocationName(const Word& word) {
|
| return false;
|
| }
|
|
|
| +} // namespace internal
|
| +
|
| +} // namespace address_parser
|
| +
|
| } // namespace content
|
|
|