Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(63)

Side by Side Diff: chrome/browser/history/query_parser.cc

Issue 9381019: Revert 121423 - Don't strip punctuation inside quotes in history search queries. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | chrome/browser/history/query_parser_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 "chrome/browser/history/query_parser.h" 5 #include "chrome/browser/history/query_parser.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/compiler_specific.h" 9 #include "base/compiler_specific.h"
10 #include "base/i18n/break_iterator.h" 10 #include "base/i18n/break_iterator.h"
11 #include "base/i18n/case_conversion.h" 11 #include "base/i18n/case_conversion.h"
12 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/stl_util.h" 13 #include "base/stl_util.h"
14 #include "base/string_util.h"
15 14
16 namespace { 15 namespace {
17 16
18 // Returns true if |mp1.first| is less than |mp2.first|. This is used to 17 // Returns true if |mp1.first| is less than |mp2.first|. This is used to
19 // sort match positions. 18 // sort match positions.
20 int CompareMatchPosition(const Snippet::MatchPosition& mp1, 19 int CompareMatchPosition(const Snippet::MatchPosition& mp1,
21 const Snippet::MatchPosition& mp2) { 20 const Snippet::MatchPosition& mp2) {
22 return mp1.first < mp2.first; 21 return mp1.first < mp2.first;
23 } 22 }
24 23
(...skipping 15 matching lines...) Expand all
40 mp.second = i->second; 39 mp.second = i->second;
41 i = matches->erase(i); 40 i = matches->erase(i);
42 } else { 41 } else {
43 return; 42 return;
44 } 43 }
45 } 44 }
46 } 45 }
47 46
48 // Sorts the match positions in |matches| by their first index, then coalesces 47 // Sorts the match positions in |matches| by their first index, then coalesces
49 // any match positions that intersect each other. 48 // any match positions that intersect each other.
50 void CoalesceAndSortMatchPositions(Snippet::MatchPositions* matches) { 49 void CoalseAndSortMatchPositions(Snippet::MatchPositions* matches) {
51 std::sort(matches->begin(), matches->end(), &CompareMatchPosition); 50 std::sort(matches->begin(), matches->end(), &CompareMatchPosition);
52 // WARNING: we don't use iterator here as CoalesceMatchesFrom may remove 51 // WARNING: we don't use iterator here as CoalesceMatchesFrom may remove
53 // from matches. 52 // from matches.
54 for (size_t i = 0; i < matches->size(); ++i) 53 for (size_t i = 0; i < matches->size(); ++i)
55 CoalesceMatchesFrom(i, matches); 54 CoalesceMatchesFrom(i, matches);
56 } 55 }
57 56
58 // Returns true if the character is considered a quote. 57 // Returns true if the character is considered a quote.
59 bool IsQueryQuote(wchar_t ch) { 58 bool IsQueryQuote(wchar_t ch) {
60 return ch == '"' || 59 return ch == '"' ||
61 ch == 0xab || // left pointing double angle bracket 60 ch == 0xab || // left pointing double angle bracket
62 ch == 0xbb || // right pointing double angle bracket 61 ch == 0xbb || // right pointing double angle bracket
63 ch == 0x201c || // left double quotation mark 62 ch == 0x201c || // left double quotation mark
64 ch == 0x201d || // right double quotation mark 63 ch == 0x201d || // right double quotation mark
65 ch == 0x201e; // double low-9 quotation mark 64 ch == 0x201e; // double low-9 quotation mark
66 } 65 }
67 66
68 // Returns true if the character is considered non-breaking when it appears in
69 // the middle of a word. This can be used to prevent an URL-like query from
70 // being broken into multiple words.
71 bool IsNonBreakingSymbol(wchar_t ch) {
72 return ch == '.' || ch == '-';
73 }
74
75 } // namespace 67 } // namespace
76 68
77 // Inheritance structure: 69 // Inheritance structure:
78 // Queries are represented as trees of QueryNodes. 70 // Queries are represented as trees of QueryNodes.
79 // QueryNodes are either a collection of subnodes (a QueryNodeList) 71 // QueryNodes are either a collection of subnodes (a QueryNodeList)
80 // or a single word (a QueryNodeWord). 72 // or a single word (a QueryNodeWord).
81 73
82 // A QueryNodeWord is a sequence of consecutive characters in a query. 74 // A QueryNodeWord is a single word in the query.
83 // It can be an actual word or an URL-like sequence of characters.
84 class QueryNodeWord : public QueryNode { 75 class QueryNodeWord : public QueryNode {
85 public: 76 public:
86 explicit QueryNodeWord(const string16& word); 77 explicit QueryNodeWord(const string16& word);
87 virtual ~QueryNodeWord(); 78 virtual ~QueryNodeWord();
88 79
89 const string16& word() const { return word_; } 80 const string16& word() const { return word_; }
90 81
91 void set_literal(bool literal) { literal_ = literal; } 82 void set_literal(bool literal) { literal_ = literal; }
92 void Append(const string16& word);
93 83
94 // QueryNode: 84 // QueryNode:
95 virtual int AppendToSQLiteQuery(string16* query) const OVERRIDE; 85 virtual int AppendToSQLiteQuery(string16* query) const OVERRIDE;
96 virtual bool IsWord() const OVERRIDE; 86 virtual bool IsWord() const OVERRIDE;
97 virtual bool Matches(const string16& word, bool exact) const OVERRIDE; 87 virtual bool Matches(const string16& word, bool exact) const OVERRIDE;
98 virtual bool HasMatchIn( 88 virtual bool HasMatchIn(
99 const std::vector<QueryWord>& words, 89 const std::vector<QueryWord>& words,
100 Snippet::MatchPositions* match_positions) const OVERRIDE; 90 Snippet::MatchPositions* match_positions) const OVERRIDE;
101 virtual void AppendWords(std::vector<string16>* words) const OVERRIDE; 91 virtual void AppendWords(std::vector<string16>* words) const OVERRIDE;
102 92
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 return true; 134 return true;
145 } 135 }
146 } 136 }
147 return false; 137 return false;
148 } 138 }
149 139
150 void QueryNodeWord::AppendWords(std::vector<string16>* words) const { 140 void QueryNodeWord::AppendWords(std::vector<string16>* words) const {
151 words->push_back(word_); 141 words->push_back(word_);
152 } 142 }
153 143
154 void QueryNodeWord::Append(const string16& str) {
155 word_ += str;
156 }
157
158 // A QueryNodeList has a collection of QueryNodes which are deleted in the end. 144 // A QueryNodeList has a collection of QueryNodes which are deleted in the end.
159 class QueryNodeList : public QueryNode { 145 class QueryNodeList : public QueryNode {
160 public: 146 public:
161 typedef std::vector<QueryNode*> QueryNodeVector; 147 typedef std::vector<QueryNode*> QueryNodeVector;
162 148
163 QueryNodeList(); 149 QueryNodeList();
164 virtual ~QueryNodeList(); 150 virtual ~QueryNodeList();
165 151
166 QueryNodeVector* children() { return &children_; } 152 QueryNodeVector* children() { return &children_; }
167 153
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after
354 if (!query_nodes[i]->HasMatchIn(query_words, &matches)) 340 if (!query_nodes[i]->HasMatchIn(query_words, &matches))
355 return false; 341 return false;
356 } 342 }
357 if (lower_text.length() != text.length()) { 343 if (lower_text.length() != text.length()) {
358 // The lower case string differs from the original string. The matches are 344 // The lower case string differs from the original string. The matches are
359 // meaningless. 345 // meaningless.
360 // TODO(sky): we need a better way to align the positions so that we don't 346 // TODO(sky): we need a better way to align the positions so that we don't
361 // completely punt here. 347 // completely punt here.
362 match_positions->clear(); 348 match_positions->clear();
363 } else { 349 } else {
364 CoalesceAndSortMatchPositions(&matches); 350 CoalseAndSortMatchPositions(&matches);
365 match_positions->swap(matches); 351 match_positions->swap(matches);
366 } 352 }
367 return true; 353 return true;
368 } 354 }
369 355
370 bool QueryParser::ParseQueryImpl(const string16& query, QueryNodeList* root) { 356 bool QueryParser::ParseQueryImpl(const string16& query, QueryNodeList* root) {
371 base::i18n::BreakIterator iter(query, base::i18n::BreakIterator::BREAK_WORD); 357 base::i18n::BreakIterator iter(query, base::i18n::BreakIterator::BREAK_WORD);
372 // TODO(evanm): support a locale here 358 // TODO(evanm): support a locale here
373 if (!iter.Init()) 359 if (!iter.Init())
374 return false; 360 return false;
375 361
376 // To handle nesting, we maintain a stack of QueryNodeLists. 362 // To handle nesting, we maintain a stack of QueryNodeLists.
377 // The last element (back) of the stack contains the current, deepest node. 363 // The last element (back) of the stack contains the current, deepest node.
378 std::vector<QueryNodeList*> query_stack; 364 std::vector<QueryNodeList*> query_stack;
379 query_stack.push_back(root); 365 query_stack.push_back(root);
380 366
381 bool in_quotes = false; // whether we're currently in a quoted phrase 367 bool in_quotes = false; // whether we're currently in a quoted phrase
382 QueryNodeWord* current_word = NULL;
383 while (iter.Advance()) { 368 while (iter.Advance()) {
384 // Just found a span between 'prev' (inclusive) and 'pos' (exclusive). It 369 // Just found a span between 'prev' (inclusive) and 'pos' (exclusive). It
385 // is not necessarily a word, it could also be a punctuation or whitespace 370 // is not necessarily a word, but could also be a sequence of punctuation
386 // character. Punctuation is preserved inside quotes, and otherwise removed 371 // or whitespace.
387 // except if it is a non-breaking character in the middle of a word. 372 if (iter.IsWord()) {
388 373 QueryNodeWord* word_node = new QueryNodeWord(iter.GetString());
389 wchar_t last_char = query[iter.prev()]; 374 if (in_quotes)
390 if (IsQueryQuote(last_char)) { 375 word_node->set_literal(true);
391 if (!in_quotes) { 376 query_stack.back()->AddChild(word_node);
392 QueryNodeList* quotes_node = new QueryNodePhrase; 377 } else { // Punctuation.
393 query_stack.back()->AddChild(quotes_node); 378 if (IsQueryQuote(query[iter.prev()])) {
394 query_stack.push_back(quotes_node); 379 if (!in_quotes) {
395 in_quotes = true; 380 QueryNodeList* quotes_node = new QueryNodePhrase;
396 } else { 381 query_stack.back()->AddChild(quotes_node);
397 query_stack.pop_back(); // Stop adding to the quoted phrase. 382 query_stack.push_back(quotes_node);
398 in_quotes = false; 383 in_quotes = true;
384 } else {
385 query_stack.pop_back(); // Stop adding to the quoted phrase.
386 in_quotes = false;
387 }
399 } 388 }
400 current_word = NULL;
401 } else if (iter.IsWord() || (in_quotes && !IsWhitespace(last_char))) {
402 // Append to the current word if the new token is a word or a non-
403 // whitespace character inside quotes.
404 if (current_word) {
405 current_word->Append(iter.GetString());
406 } else {
407 current_word = new QueryNodeWord(iter.GetString());
408 current_word->set_literal(in_quotes);
409 query_stack.back()->AddChild(current_word);
410 }
411 } else if (current_word != NULL) {
412 // Allow non-breaking symbols inside a word.
413 // Any other punctuation or whitespace character ends the current word.
414
415 // TODO(dubroy): Consider sharing code with the omnibox to allow a more
416 // accurate "best guess" of whether a sequence of characters is a URL.
417 if (IsNonBreakingSymbol(last_char))
418 current_word->Append(iter.GetString());
419 else
420 current_word = NULL;
421 } 389 }
422 } 390 }
423 391
424 root->RemoveEmptySubnodes(); 392 root->RemoveEmptySubnodes();
425 return true; 393 return true;
426 } 394 }
427 395
428 void QueryParser::ExtractQueryWords(const string16& text, 396 void QueryParser::ExtractQueryWords(const string16& text,
429 std::vector<QueryWord>* words) { 397 std::vector<QueryWord>* words) {
430 base::i18n::BreakIterator iter(text, base::i18n::BreakIterator::BREAK_WORD); 398 base::i18n::BreakIterator iter(text, base::i18n::BreakIterator::BREAK_WORD);
431 // TODO(evanm): support a locale here 399 // TODO(evanm): support a locale here
432 if (!iter.Init()) 400 if (!iter.Init())
433 return; 401 return;
434 402
435 while (iter.Advance()) { 403 while (iter.Advance()) {
436 // Just found a span between 'prev' (inclusive) and 'pos' (exclusive). It 404 // Just found a span between 'prev' (inclusive) and 'pos' (exclusive). It
437 // is not necessarily a word, but could also be a sequence of punctuation 405 // is not necessarily a word, but could also be a sequence of punctuation
438 // or whitespace. 406 // or whitespace.
439 if (iter.IsWord()) { 407 if (iter.IsWord()) {
440 string16 word = iter.GetString(); 408 string16 word = iter.GetString();
441 if (!word.empty()) { 409 if (!word.empty()) {
442 words->push_back(QueryWord()); 410 words->push_back(QueryWord());
443 words->back().word = word; 411 words->back().word = word;
444 words->back().position = iter.prev(); 412 words->back().position = iter.prev();
445 } 413 }
446 } 414 }
447 } 415 }
448 } 416 }
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/history/query_parser_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698