<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/history/query_parser.h"

#include <algorithm>

#include "base/i18n/break_iterator.h"
#include "base/logging.h"
#include "base/memory/scoped_vector.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "ui/base/l10n/l10n_util.h"
#include "unicode/uscript.h"

namespace {

// Returns true if |mp1.first| is less than |mp2.first|. This is used to
// sort match positions.
int CompareMatchPosition(const Snippet::MatchPosition& mp1,
                         const Snippet::MatchPosition& mp2) {
  return mp1.first < mp2.first;
}

// Returns true if |mp2| intersects |mp1|. This is intended for use by
// CoalesceMatchesFrom and isn't meant as a general intersectpion comparison
// function.
bool SnippetIntersects(const Snippet::MatchPosition& mp1,
                       const Snippet::MatchPosition& mp2) {
  return mp2.first >= mp1.first && mp2.first <= mp1.second;
}

// Coalesces match positions in |matches| after index that intersect the match
// position at |index|.
void CoalesceMatchesFrom(size_t index,
                         Snippet::MatchPositions* matches) {
  Snippet::MatchPosition& mp = (*matches)[index];
  for (Snippet::MatchPositions::iterator i = matches->begin() + index + 1;
       i != matches->end(); ) {
    if (SnippetIntersects(mp, *i)) {
      mp.second = i->second;
      i = matches->erase(i);
    } else {
      return;
    }
  }
}

// Sorts the match positions in |matches| by their first index, then coalesces
// any match positions that intersect each other.
void CoalseAndSortMatchPositions(Snippet::MatchPositions* matches) {
  std::sort(matches->begin(), matches->end(), &CompareMatchPosition);
  // WARNING: we don't use iterator here as CoalesceMatchesFrom may remove
  // from matches.
  for (size_t i = 0; i < matches->size(); ++i)
    CoalesceMatchesFrom(i, matches);
}

}  // namespace

// Inheritance structure:
// Queries are represented as trees of QueryNodes.
// QueryNodes are either a collection of subnodes (a QueryNodeList)
// or a single word (a QueryNodeWord).

// A QueryNodeWord is a single word in the query.
class QueryNodeWord : public QueryNode {
 public:
  explicit QueryNodeWord(const string16& word)
      : word_(word), literal_(false) {}
  virtual ~QueryNodeWord() {}
  virtual int AppendToSQLiteQuery(string16* query) const;
  virtual bool IsWord() const { return true; }

  const string16& word() const { return word_; }
  void set_literal(bool literal) { literal_ = literal; }

  virtual bool HasMatchIn(const std::vector<QueryWord>& words,
                          Snippet::MatchPositions* match_positions) const;

  virtual bool Matches(const string16& word, bool exact) const;
  virtual void AppendWords(std::vector<string16>* words) const;

 private:
  string16 word_;
  bool literal_;
};

bool QueryNodeWord::HasMatchIn(const std::vector<QueryWord>& words,
                               Snippet::MatchPositions* match_positions) const {
  for (size_t i = 0; i < words.size(); ++i) {
    if (Matches(words[i].word, false)) {
      size_t match_start = words[i].position;
      match_positions->push_back(
          Snippet::MatchPosition(match_start,
                                 match_start + static_cast<int>(word_.size())));
      return true;
    }
  }
  return false;
}

bool QueryNodeWord::Matches(const string16& word, bool exact) const {
  if (exact || !QueryParser::IsWordLongEnoughForPrefixSearch(word_))
    return word == word_;
  return word.size() >= word_.size() &&
         (word_.compare(0, word_.size(), word, 0, word_.size()) == 0);
}

void QueryNodeWord::AppendWords(std::vector<string16>* words) const {
  words->push_back(word_);
}

int QueryNodeWord::AppendToSQLiteQuery(string16* query) const {
  query->append(word_);

  // Use prefix search if we're not literal and long enough.
  if (!literal_ && QueryParser::IsWordLongEnoughForPrefixSearch(word_))
    *query += L'*';
  return 1;
}

// A QueryNodeList has a collection of child QueryNodes
// which it cleans up after.
class QueryNodeList : public QueryNode {
 public:
  virtual ~QueryNodeList();

  virtual int AppendToSQLiteQuery(string16* query) const {
    return AppendChildrenToString(query);
  }
  virtual bool IsWord() const { return false; }

  void AddChild(QueryNode* node) { children_.push_back(node); }

  typedef std::vector<QueryNode*> QueryNodeVector;
  QueryNodeVector* children() { return &children_; }

  // Remove empty subnodes left over from other parsing.
  void RemoveEmptySubnodes();

  // QueryNodeList is never used with Matches or HasMatchIn.
  virtual bool Matches(const string16& word, bool exact) const {
    NOTREACHED();
    return false;
  }
  virtual bool HasMatchIn(const std::vector<QueryWord>& words,
                          Snippet::MatchPositions* match_positions) const {
    NOTREACHED();
    return false;
  }
  virtual void AppendWords(std::vector<string16>* words) const;

 protected:
  int AppendChildrenToString(string16* query) const;

  QueryNodeVector children_;
};

QueryNodeList::~QueryNodeList() {
  for (QueryNodeVector::iterator node = children_.begin();
       node != children_.end(); ++node)
    delete *node;
}

void QueryNodeList::RemoveEmptySubnodes() {
  for (size_t i = 0; i < children_.size(); ++i) {
    if (children_[i]->IsWord())
      continue;

    QueryNodeList* list_node = static_cast<QueryNodeList*>(children_[i]);
    list_node->RemoveEmptySubnodes();
    if (list_node->children()->empty()) {
      children_.erase(children_.begin() + i);
      --i;
      delete list_node;
    }
  }
}

void QueryNodeList::AppendWords(std::vector<string16>* words) const {
  for (size_t i = 0; i < children_.size(); ++i)
    children_[i]->AppendWords(words);
}

int QueryNodeList::AppendChildrenToString(string16* query) const {
  int num_words = 0;
  for (QueryNodeVector::const_iterator node = children_.begin();
       node != children_.end(); ++node) {
    if (node != children_.begin())
      query->push_back(L' ');
    num_words += (*node)->AppendToSQLiteQuery(query);
  }
  return num_words;
}

// A QueryNodePhrase is a phrase query ("quoted").
class QueryNodePhrase : public QueryNodeList {
 public:
  virtual int AppendToSQLiteQuery(string16* query) const {
    query->push_back(L'"');
    int num_words = AppendChildrenToString(query);
    query->push_back(L'"');
    return num_words;
  }

  virtual bool Matches(const string16& word, bool exact) const;
  virtual bool HasMatchIn(const std::vector<QueryWord>& words,
                          Snippet::MatchPositions* match_positions) const;
};

bool QueryNodePhrase::Matches(const string16& word, bool exact) const {
  NOTREACHED();
  return false;
}

bool QueryNodePhrase::HasMatchIn(
    const std::vector<QueryWord>& words,
    Snippet::MatchPositions* match_positions) const {
  if (words.size() < children_.size())
    return false;

  for (size_t i = 0, max = words.size() - children_.size() + 1; i < max; ++i) {
    bool matched_all = true;
    for (size_t j = 0; j < children_.size(); ++j) {
      if (!children_[j]->Matches(words[i + j].word, true)) {
        matched_all = false;
        break;
      }
    }
    if (matched_all) {
      const QueryWord& last_word = words[i + children_.size() - 1];
      match_positions->push_back(
          Snippet::MatchPosition(words[i].position,
                                 last_word.position + last_word.word.length()));
      return true;
    }
  }
  return false;
}

QueryParser::QueryParser() {
}

// static
bool QueryParser::IsWordLongEnoughForPrefixSearch(const string16& word) {
  DCHECK(!word.empty());
  size_t minimum_length = 3;
  // We intentionally exclude Hangul Jamos (both Conjoining and compatibility)
  // because they 'behave like' Latin letters. Moreover, we should
  // normalize the former before reaching here.
  if (0xAC00 <= word[0] && word[0] <= 0xD7A3)
    minimum_length = 2;
  return word.size() >= minimum_length;
}

// Returns true if the character is considered a quote.
static bool IsQueryQuote(wchar_t ch) {
  return ch == '"' ||
         ch == 0xab ||    // left pointing double angle bracket
         ch == 0xbb ||    // right pointing double angle bracket
         ch == 0x201c ||  // left double quotation mark
         ch == 0x201d ||  // right double quotation mark
         ch == 0x201e;    // double low-9 quotation mark
}

int QueryParser::ParseQuery(const string16& query,
                            string16* sqlite_query) {
  QueryNodeList root;
  if (!ParseQueryImpl(query, &root))
    return 0;
  return root.AppendToSQLiteQuery(sqlite_query);
}

void QueryParser::ParseQuery(const string16& query,
                             std::vector<QueryNode*>* nodes) {
  QueryNodeList root;
  if (ParseQueryImpl(l10n_util::ToLower(query), &root))
    nodes->swap(*root.children());
}

void QueryParser::ExtractQueryWords(const string16& query,
                                    std::vector<string16>* words) {
  QueryNodeList root;
  if (!ParseQueryImpl(query, &root))
    return;
  root.AppendWords(words);
}

bool QueryParser::DoesQueryMatch(const string16& text,
                                 const std::vector<QueryNode*>& query_nodes,
                                 Snippet::MatchPositions* match_positions) {
  if (query_nodes.empty())
    return false;

  std::vector<QueryWord> query_words;
  string16 lower_text = l10n_util::ToLower(text);
  ExtractQueryWords(lower_text, &query_words);

  if (query_words.empty())
    return false;

  Snippet::MatchPositions matches;
  for (size_t i = 0; i < query_nodes.size(); ++i) {
    if (!query_nodes[i]->HasMatchIn(query_words, &matches))
      return false;
  }
  if (lower_text.length() != text.length()) {
    // The lower case string differs from the original string. The matches are
    // meaningless.
    // TODO(sky): we need a better way to align the positions so that we don't
    // completely punt here.
    match_positions->clear();
  } else {
    CoalseAndSortMatchPositions(&matches);
    match_positions->swap(matches);
  }
  return true;
}

bool QueryParser::ParseQueryImpl(const string16& query,
                                QueryNodeList* root) {
  base::BreakIterator iter(&query, base::BreakIterator::BREAK_WORD);
  // TODO(evanm): support a locale here
  if (!iter.Init())
    return false;

  // To handle nesting, we maintain a stack of QueryNodeLists.
  // The last element (back) of the stack contains the current, deepest node.
  std::vector<QueryNodeList*> query_stack;
  query_stack.push_back(root);

  bool in_quotes = false;  // whether we're currently in a quoted phrase
  while (iter.Advance()) {
    // Just found a span between 'prev' (inclusive) and 'pos' (exclusive). It
    // is not necessarily a word, but could also be a sequence of punctuation
    // or whitespace.
    if (iter.IsWord()) {
      string16 word = iter.GetString();

      QueryNodeWord* word_node = new QueryNodeWord(word);
      if (in_quotes)
        word_node->set_literal(true);
      query_stack.back()->AddChild(word_node);
    } else {  // Punctuation.
      if (IsQueryQuote(query[iter.prev()])) {
        if (!in_quotes) {
          QueryNodeList* quotes_node = new QueryNodePhrase;
          query_stack.back()->AddChild(quotes_node);
          query_stack.push_back(quotes_node);
          in_quotes = true;
        } else {
          query_stack.pop_back();  // Stop adding to the quoted phrase.
          in_quotes = false;
        }
      }
    }
  }

  root->RemoveEmptySubnodes();
  return true;
}

void QueryParser::ExtractQueryWords(const string16& text,
                                    std::vector<QueryWord>* words) {
  base::BreakIterator iter(&text, base::BreakIterator::BREAK_WORD);
  // TODO(evanm): support a locale here
  if (!iter.Init())
    return;

  while (iter.Advance()) {
    // Just found a span between 'prev' (inclusive) and 'pos' (exclusive). It
    // is not necessarily a word, but could also be a sequence of punctuation
    // or whitespace.
    if (iter.IsWord()) {
      string16 word = iter.GetString();
      if (!word.empty()) {
        words->push_back(QueryWord());
        words->back().word = word;
        words->back().position = iter.prev();
      }
    }
  }
}
<commit_msg>history: Make QueryNodeList more readable.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/history/query_parser.h"

#include <algorithm>

#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/i18n/break_iterator.h"
#include "base/logging.h"
#include "base/memory/scoped_vector.h"
#include "base/stl_util-inl.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "ui/base/l10n/l10n_util.h"
#include "unicode/uscript.h"

namespace {

// Returns true if |mp1.first| is less than |mp2.first|. This is used to
// sort match positions.
int CompareMatchPosition(const Snippet::MatchPosition& mp1,
                         const Snippet::MatchPosition& mp2) {
  return mp1.first < mp2.first;
}

// Returns true if |mp2| intersects |mp1|. This is intended for use by
// CoalesceMatchesFrom and isn't meant as a general intersectpion comparison
// function.
bool SnippetIntersects(const Snippet::MatchPosition& mp1,
                       const Snippet::MatchPosition& mp2) {
  return mp2.first >= mp1.first && mp2.first <= mp1.second;
}

// Coalesces match positions in |matches| after index that intersect the match
// position at |index|.
void CoalesceMatchesFrom(size_t index,
                         Snippet::MatchPositions* matches) {
  Snippet::MatchPosition& mp = (*matches)[index];
  for (Snippet::MatchPositions::iterator i = matches->begin() + index + 1;
       i != matches->end(); ) {
    if (SnippetIntersects(mp, *i)) {
      mp.second = i->second;
      i = matches->erase(i);
    } else {
      return;
    }
  }
}

// Sorts the match positions in |matches| by their first index, then coalesces
// any match positions that intersect each other.
void CoalseAndSortMatchPositions(Snippet::MatchPositions* matches) {
  std::sort(matches->begin(), matches->end(), &CompareMatchPosition);
  // WARNING: we don't use iterator here as CoalesceMatchesFrom may remove
  // from matches.
  for (size_t i = 0; i < matches->size(); ++i)
    CoalesceMatchesFrom(i, matches);
}

}  // namespace

// Inheritance structure:
// Queries are represented as trees of QueryNodes.
// QueryNodes are either a collection of subnodes (a QueryNodeList)
// or a single word (a QueryNodeWord).

// A QueryNodeWord is a single word in the query.
class QueryNodeWord : public QueryNode {
 public:
  explicit QueryNodeWord(const string16& word)
      : word_(word), literal_(false) {}
  virtual ~QueryNodeWord() {}
  virtual int AppendToSQLiteQuery(string16* query) const;
  virtual bool IsWord() const { return true; }

  const string16& word() const { return word_; }
  void set_literal(bool literal) { literal_ = literal; }

  virtual bool HasMatchIn(const std::vector<QueryWord>& words,
                          Snippet::MatchPositions* match_positions) const;

  virtual bool Matches(const string16& word, bool exact) const;
  virtual void AppendWords(std::vector<string16>* words) const;

 private:
  string16 word_;
  bool literal_;
};

bool QueryNodeWord::HasMatchIn(const std::vector<QueryWord>& words,
                               Snippet::MatchPositions* match_positions) const {
  for (size_t i = 0; i < words.size(); ++i) {
    if (Matches(words[i].word, false)) {
      size_t match_start = words[i].position;
      match_positions->push_back(
          Snippet::MatchPosition(match_start,
                                 match_start + static_cast<int>(word_.size())));
      return true;
    }
  }
  return false;
}

bool QueryNodeWord::Matches(const string16& word, bool exact) const {
  if (exact || !QueryParser::IsWordLongEnoughForPrefixSearch(word_))
    return word == word_;
  return word.size() >= word_.size() &&
         (word_.compare(0, word_.size(), word, 0, word_.size()) == 0);
}

void QueryNodeWord::AppendWords(std::vector<string16>* words) const {
  words->push_back(word_);
}

int QueryNodeWord::AppendToSQLiteQuery(string16* query) const {
  query->append(word_);

  // Use prefix search if we're not literal and long enough.
  if (!literal_ && QueryParser::IsWordLongEnoughForPrefixSearch(word_))
    *query += L'*';
  return 1;
}

// A QueryNodeList has a collection of QueryNodes which are deleted in the end.
class QueryNodeList : public QueryNode {
 public:
  typedef std::vector<QueryNode*> QueryNodeVector;

  QueryNodeList();
  virtual ~QueryNodeList();

  QueryNodeVector* children() { return &children_; }

  void AddChild(QueryNode* node);

  // Remove empty subnodes left over from other parsing.
  void RemoveEmptySubnodes();

  // QueryNode:
  virtual int AppendToSQLiteQuery(string16* query) const OVERRIDE;
  virtual bool IsWord() const OVERRIDE;
  virtual bool Matches(const string16& word, bool exact) const OVERRIDE;
  virtual bool HasMatchIn(
      const std::vector<QueryWord>& words,
      Snippet::MatchPositions* match_positions) const OVERRIDE;
  virtual void AppendWords(std::vector<string16>* words) const OVERRIDE;

 protected:
  int AppendChildrenToString(string16* query) const;

  QueryNodeVector children_;

 private:
  DISALLOW_COPY_AND_ASSIGN(QueryNodeList);
};

QueryNodeList::QueryNodeList() {}

QueryNodeList::~QueryNodeList() {
  STLDeleteElements(&children_);
}

void QueryNodeList::AddChild(QueryNode* node) {
  children_.push_back(node);
}

void QueryNodeList::RemoveEmptySubnodes() {
  for (size_t i = 0; i < children_.size(); ++i) {
    if (children_[i]->IsWord())
      continue;

    QueryNodeList* list_node = static_cast<QueryNodeList*>(children_[i]);
    list_node->RemoveEmptySubnodes();
    if (list_node->children()->empty()) {
      children_.erase(children_.begin() + i);
      --i;
      delete list_node;
    }
  }
}

int QueryNodeList::AppendToSQLiteQuery(string16* query) const {
  return AppendChildrenToString(query);
}

bool QueryNodeList::IsWord() const {
  return false;
}

bool QueryNodeList::Matches(const string16& word, bool exact) const {
  NOTREACHED();
  return false;
}

bool QueryNodeList::HasMatchIn(const std::vector<QueryWord>& words,
                               Snippet::MatchPositions* match_positions) const {
  NOTREACHED();
  return false;
}

void QueryNodeList::AppendWords(std::vector<string16>* words) const {
  for (size_t i = 0; i < children_.size(); ++i)
    children_[i]->AppendWords(words);
}

int QueryNodeList::AppendChildrenToString(string16* query) const {
  int num_words = 0;
  for (QueryNodeVector::const_iterator node = children_.begin();
       node != children_.end(); ++node) {
    if (node != children_.begin())
      query->push_back(L' ');
    num_words += (*node)->AppendToSQLiteQuery(query);
  }
  return num_words;
}

// A QueryNodePhrase is a phrase query ("quoted").
class QueryNodePhrase : public QueryNodeList {
 public:
  virtual int AppendToSQLiteQuery(string16* query) const {
    query->push_back(L'"');
    int num_words = AppendChildrenToString(query);
    query->push_back(L'"');
    return num_words;
  }

  virtual bool Matches(const string16& word, bool exact) const;
  virtual bool HasMatchIn(const std::vector<QueryWord>& words,
                          Snippet::MatchPositions* match_positions) const;
};

bool QueryNodePhrase::Matches(const string16& word, bool exact) const {
  NOTREACHED();
  return false;
}

bool QueryNodePhrase::HasMatchIn(
    const std::vector<QueryWord>& words,
    Snippet::MatchPositions* match_positions) const {
  if (words.size() < children_.size())
    return false;

  for (size_t i = 0, max = words.size() - children_.size() + 1; i < max; ++i) {
    bool matched_all = true;
    for (size_t j = 0; j < children_.size(); ++j) {
      if (!children_[j]->Matches(words[i + j].word, true)) {
        matched_all = false;
        break;
      }
    }
    if (matched_all) {
      const QueryWord& last_word = words[i + children_.size() - 1];
      match_positions->push_back(
          Snippet::MatchPosition(words[i].position,
                                 last_word.position + last_word.word.length()));
      return true;
    }
  }
  return false;
}

QueryParser::QueryParser() {
}

// static
bool QueryParser::IsWordLongEnoughForPrefixSearch(const string16& word) {
  DCHECK(!word.empty());
  size_t minimum_length = 3;
  // We intentionally exclude Hangul Jamos (both Conjoining and compatibility)
  // because they 'behave like' Latin letters. Moreover, we should
  // normalize the former before reaching here.
  if (0xAC00 <= word[0] && word[0] <= 0xD7A3)
    minimum_length = 2;
  return word.size() >= minimum_length;
}

// Returns true if the character is considered a quote.
static bool IsQueryQuote(wchar_t ch) {
  return ch == '"' ||
         ch == 0xab ||    // left pointing double angle bracket
         ch == 0xbb ||    // right pointing double angle bracket
         ch == 0x201c ||  // left double quotation mark
         ch == 0x201d ||  // right double quotation mark
         ch == 0x201e;    // double low-9 quotation mark
}

int QueryParser::ParseQuery(const string16& query,
                            string16* sqlite_query) {
  QueryNodeList root;
  if (!ParseQueryImpl(query, &root))
    return 0;
  return root.AppendToSQLiteQuery(sqlite_query);
}

void QueryParser::ParseQuery(const string16& query,
                             std::vector<QueryNode*>* nodes) {
  QueryNodeList root;
  if (ParseQueryImpl(l10n_util::ToLower(query), &root))
    nodes->swap(*root.children());
}

void QueryParser::ExtractQueryWords(const string16& query,
                                    std::vector<string16>* words) {
  QueryNodeList root;
  if (!ParseQueryImpl(query, &root))
    return;
  root.AppendWords(words);
}

bool QueryParser::DoesQueryMatch(const string16& text,
                                 const std::vector<QueryNode*>& query_nodes,
                                 Snippet::MatchPositions* match_positions) {
  if (query_nodes.empty())
    return false;

  std::vector<QueryWord> query_words;
  string16 lower_text = l10n_util::ToLower(text);
  ExtractQueryWords(lower_text, &query_words);

  if (query_words.empty())
    return false;

  Snippet::MatchPositions matches;
  for (size_t i = 0; i < query_nodes.size(); ++i) {
    if (!query_nodes[i]->HasMatchIn(query_words, &matches))
      return false;
  }
  if (lower_text.length() != text.length()) {
    // The lower case string differs from the original string. The matches are
    // meaningless.
    // TODO(sky): we need a better way to align the positions so that we don't
    // completely punt here.
    match_positions->clear();
  } else {
    CoalseAndSortMatchPositions(&matches);
    match_positions->swap(matches);
  }
  return true;
}

bool QueryParser::ParseQueryImpl(const string16& query,
                                QueryNodeList* root) {
  base::BreakIterator iter(&query, base::BreakIterator::BREAK_WORD);
  // TODO(evanm): support a locale here
  if (!iter.Init())
    return false;

  // To handle nesting, we maintain a stack of QueryNodeLists.
  // The last element (back) of the stack contains the current, deepest node.
  std::vector<QueryNodeList*> query_stack;
  query_stack.push_back(root);

  bool in_quotes = false;  // whether we're currently in a quoted phrase
  while (iter.Advance()) {
    // Just found a span between 'prev' (inclusive) and 'pos' (exclusive). It
    // is not necessarily a word, but could also be a sequence of punctuation
    // or whitespace.
    if (iter.IsWord()) {
      string16 word = iter.GetString();

      QueryNodeWord* word_node = new QueryNodeWord(word);
      if (in_quotes)
        word_node->set_literal(true);
      query_stack.back()->AddChild(word_node);
    } else {  // Punctuation.
      if (IsQueryQuote(query[iter.prev()])) {
        if (!in_quotes) {
          QueryNodeList* quotes_node = new QueryNodePhrase;
          query_stack.back()->AddChild(quotes_node);
          query_stack.push_back(quotes_node);
          in_quotes = true;
        } else {
          query_stack.pop_back();  // Stop adding to the quoted phrase.
          in_quotes = false;
        }
      }
    }
  }

  root->RemoveEmptySubnodes();
  return true;
}

void QueryParser::ExtractQueryWords(const string16& text,
                                    std::vector<QueryWord>* words) {
  base::BreakIterator iter(&text, base::BreakIterator::BREAK_WORD);
  // TODO(evanm): support a locale here
  if (!iter.Init())
    return;

  while (iter.Advance()) {
    // Just found a span between 'prev' (inclusive) and 'pos' (exclusive). It
    // is not necessarily a word, but could also be a sequence of punctuation
    // or whitespace.
    if (iter.IsWord()) {
      string16 word = iter.GetString();
      if (!word.empty()) {
        words->push_back(QueryWord());
        words->back().word = word;
        words->back().position = iter.prev();
      }
    }
  }
}
<|endoftext|>


<commit_before>#include <czmq.h>
#include <g2log.hpp>
#include <algorithm>

#include "FileRecv.h"

FileRecv::FileRecv():
mQueueLength(10),
mTimeoutMs(30000), //5 minutes
mOffset(0),
mChunk(nullptr) {
   mCtx = zctx_new();
   CHECK(mCtx);
   mDealer = zsocket_new(mCtx, ZMQ_DEALER);
   CHECK(mDealer);
   mCredit = mQueueLength;
}

FileRecv::Socket FileRecv::SetLocation(const std::string& location){
   int result = zsocket_connect(mDealer, location.c_str());
   return (0 == result) ? FileRecv::Socket::OK : FileRecv::Socket::INVALID;
}

void FileRecv::SetTimeout(const int timeoutMs){
   mTimeoutMs = timeoutMs;
}

void FileRecv::RequestChunks(){
   // Send enough data requests to fill pipeline:
   while (mCredit) {
      zstr_sendf (mDealer, "%ld", mOffset);
      mOffset++;
      mCredit--;
   }
}

FileRecv::Stream FileRecv::Receive(std::vector<uint8_t>& data){
   //Erase any previous data from the last Monitor()
   FreeChunk();
   RequestChunks();
   static const std::vector<uint8_t> emptyOnError;

   //Poll to see if anything is available on the pipeline:
   if(zsocket_poll(mDealer, mTimeoutMs)){

      mChunk = zframe_recv (mDealer);
      if(!mChunk) {
         data = emptyOnError;
         //Interrupt or end of stream
         return FileRecv::Stream::INTERRUPT;
      }

      //chunk->size = zframe_size (mChunk);
      int size = zframe_size (mChunk);
      data.resize(size);
      if(size <= 0){
         //End of Stream
         return FileRecv::Stream::END_OF_STREAM;
      }

      uint8_t* raw = reinterpret_cast<uint8_t*>(zframe_data(mChunk));
      std::copy(raw, raw + size, data.begin());

      mCredit++;
      return FileRecv::Stream::CONTINUE;

   }

   data = emptyOnError;
   return FileRecv::Stream::TIMEOUT;
}

void FileRecv::FreeChunk(){
   if(mChunk != nullptr){
      zframe_destroy(&mChunk);
      mChunk = nullptr;
   }
}

FileRecv::~FileRecv(){
   FreeChunk();
   zsocket_destroy(mCtx, mDealer);
   zctx_destroy(&mCtx);
}<commit_msg>interrupt check added<commit_after>#include <czmq.h>
#include <g2log.hpp>
#include <algorithm>

#include "FileRecv.h"

FileRecv::FileRecv():
mQueueLength(10),
mTimeoutMs(30000), //5 minutes
mOffset(0),
mChunk(nullptr) {
   mCtx = zctx_new();
   CHECK(mCtx);
   mDealer = zsocket_new(mCtx, ZMQ_DEALER);
   CHECK(mDealer);
   mCredit = mQueueLength;
}

FileRecv::Socket FileRecv::SetLocation(const std::string& location){
   int result = zsocket_connect(mDealer, location.c_str());
   return (0 == result) ? FileRecv::Socket::OK : FileRecv::Socket::INVALID;
}

void FileRecv::SetTimeout(const int timeoutMs){
   mTimeoutMs = timeoutMs;
}

void FileRecv::RequestChunks(){
   // Send enough data requests to fill pipeline:
   while (mCredit && !zctx_interrupted) {
      zstr_sendf (mDealer, "%ld", mOffset);
      mOffset++;
      mCredit--;
   }
}

FileRecv::Stream FileRecv::Receive(std::vector<uint8_t>& data){
   //Erase any previous data from the last Monitor()
   FreeChunk();
   RequestChunks();
   static const std::vector<uint8_t> emptyOnError;

   //Poll to see if anything is available on the pipeline:
   if(zsocket_poll(mDealer, mTimeoutMs)){

      mChunk = zframe_recv (mDealer);
      if(!mChunk) {
         data = emptyOnError;
         //Interrupt or end of stream
         return FileRecv::Stream::INTERRUPT;
      }

      //chunk->size = zframe_size (mChunk);
      int size = zframe_size (mChunk);
      data.resize(size);
      if(size <= 0){
         //End of Stream
         return FileRecv::Stream::END_OF_STREAM;
      }

      uint8_t* raw = reinterpret_cast<uint8_t*>(zframe_data(mChunk));
      std::copy(raw, raw + size, data.begin());

      mCredit++;
      return FileRecv::Stream::CONTINUE;

   }

   data = emptyOnError;
   return FileRecv::Stream::TIMEOUT;
}

void FileRecv::FreeChunk(){
   if(mChunk != nullptr){
      zframe_destroy(&mChunk);
      mChunk = nullptr;
   }
}

FileRecv::~FileRecv(){
   FreeChunk();
   zsocket_destroy(mCtx, mDealer);
   zctx_destroy(&mCtx);
}<|endoftext|>


<commit_before><commit_msg>Revert changes to ie_importer.cc from r25498.  The builder doesn't like something; investigating.<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/signin/token_service.h"

#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/string_util.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/webdata/web_data_service_factory.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/net/gaia/gaia_auth_fetcher.h"
#include "chrome/common/net/gaia/gaia_constants.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "net/url_request/url_request_context_getter.h"

using content::BrowserThread;

namespace {

// List of services that are capable of ClientLogin-based authentication.
const char* kServices[] = {
  GaiaConstants::kGaiaService,
  GaiaConstants::kSyncService,
  GaiaConstants::kDeviceManagementService,
  GaiaConstants::kLSOService
};

}  // namespace

TokenService::TokenService()
    : profile_(NULL),
      token_loading_query_(0),
      tokens_loaded_(false) {
  // Allow constructor to be called outside the UI thread, so it can be mocked
  // out for unit tests.

  COMPILE_ASSERT(arraysize(kServices) == arraysize(fetchers_),
                 kServices_and_fetchers_dont_have_same_size);
}

TokenService::~TokenService() {
  if (!source_.empty()) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    ResetCredentialsInMemory();
  }
}

void TokenService::Initialize(const char* const source,
                              Profile* profile) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (!source_.empty()) {
    // Already initialized.
    return;
  }
  DCHECK(!profile_);
  profile_ = profile;
  getter_ = profile->GetRequestContext();
  // Since the user can create a bookmark in incognito, sync may be running.
  // Thus we have to go for explicit access.
  web_data_service_ = WebDataServiceFactory::GetForProfile(
      profile, Profile::EXPLICIT_ACCESS);
  source_ = std::string(source);

  CommandLine* cmd_line = CommandLine::ForCurrentProcess();
  // Allow the token service to be cleared from the command line. We rely on
  // SigninManager::Initialize() being called to clear out the
  // kGoogleServicesUsername pref before we call EraseTokensFromDB() as
  // otherwise the system would be in an invalid state.
  if (cmd_line->HasSwitch(switches::kClearTokenService))
    EraseTokensFromDB();

  // Allow a token to be injected from the command line.
  if (cmd_line->HasSwitch(switches::kSetToken)) {
    std::string value = cmd_line->GetSwitchValueASCII(switches::kSetToken);
    int separator = value.find(':');
    std::string service = value.substr(0, separator);
    std::string token = value.substr(separator + 1);
    token_map_[service] = token;
    SaveAuthTokenToDB(service, token);
  }

  registrar_.Add(this,
                 chrome::NOTIFICATION_TOKEN_UPDATED,
                 content::Source<Profile>(profile));
}

void TokenService::ResetCredentialsInMemory() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  // Terminate any running fetchers. Callbacks will not return.
  for (size_t i = 0; i < arraysize(kServices); ++i) {
    fetchers_[i].reset();
  }

  // Cancel pending loads. Callbacks will not return.
  if (token_loading_query_) {
    web_data_service_->CancelRequest(token_loading_query_);
    token_loading_query_ = 0;
  }

  tokens_loaded_ = false;
  token_map_.clear();
  credentials_ = GaiaAuthConsumer::ClientLoginResult();
}

void TokenService::UpdateCredentials(
    const GaiaAuthConsumer::ClientLoginResult& credentials) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  credentials_ = credentials;

  SaveAuthTokenToDB(GaiaConstants::kGaiaLsid, credentials.lsid);
  SaveAuthTokenToDB(GaiaConstants::kGaiaSid, credentials.sid);

  // Cancel any currently running requests.
  for (size_t i = 0; i < arraysize(kServices); i++) {
    fetchers_[i].reset();
  }

  // Notify the CredentialCacheService that a new lsid and sid are available.
  FireCredentialsUpdatedNotification(credentials.lsid, credentials.sid);
}

void TokenService::UpdateCredentialsWithOAuth2(
    const GaiaAuthConsumer::ClientOAuthResult& credentials) {
  // Will be implemented once the ClientOAuth signin is complete.  Not called
  // yet by any code.
  NOTREACHED();
}

void TokenService::LoadTokensFromDB() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (web_data_service_.get())
    token_loading_query_ = web_data_service_->GetAllTokens(this);
}

void TokenService::SaveAuthTokenToDB(const std::string& service,
                                     const std::string& auth_token) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (web_data_service_.get())
    web_data_service_->SetTokenForService(service, auth_token);
}

void TokenService::EraseTokensFromDB() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  // Try to track down http://crbug.com/121755 - we should never clear the
  // token DB while we're still logged in.
  if (profile_) {
    std::string user = profile_->GetPrefs()->GetString(
        prefs::kGoogleServicesUsername);
    CHECK(user.empty());
  }
  if (web_data_service_.get())
    web_data_service_->RemoveAllTokens();

  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_TOKENS_CLEARED,
      content::Source<TokenService>(this),
      content::NotificationService::NoDetails());
}

bool TokenService::TokensLoadedFromDB() const {
  return tokens_loaded_;
}

// static
int TokenService::GetServiceIndex(const std::string& service) {
  for (size_t i = 0; i < arraysize(kServices); ++i) {
    if (kServices[i] == service)
      return i;
  }
  return -1;
}

bool TokenService::AreCredentialsValid() const {
  return !credentials_.lsid.empty() && !credentials_.sid.empty();
}

void TokenService::StartFetchingTokens() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(AreCredentialsValid());
  for (size_t i = 0; i < arraysize(kServices); i++) {
    fetchers_[i].reset(new GaiaAuthFetcher(this, source_, getter_));
    fetchers_[i]->StartIssueAuthToken(credentials_.sid,
                                      credentials_.lsid,
                                      kServices[i]);
  }
}

// Services dependent on a token will check if a token is available.
// If it isn't, they'll go to sleep until they get a token event.
bool TokenService::HasTokenForService(const char* service) const {
  return token_map_.count(service) > 0;
}

const std::string& TokenService::GetTokenForService(
    const char* const service) const {

  if (token_map_.count(service) > 0) {
    // Note map[key] is not const.
    return (*token_map_.find(service)).second;
  }
  return EmptyString();
}

bool TokenService::HasOAuthLoginToken() const {
  return HasTokenForService(GaiaConstants::kGaiaOAuth2LoginRefreshToken);
}

const std::string& TokenService::GetOAuth2LoginRefreshToken() const {
  return GetTokenForService(GaiaConstants::kGaiaOAuth2LoginRefreshToken);
}

const std::string& TokenService::GetOAuth2LoginAccessToken() const {
  return GetTokenForService(GaiaConstants::kGaiaOAuth2LoginAccessToken);
}

// static
void TokenService::GetServiceNamesForTesting(std::vector<std::string>* names) {
  names->resize(arraysize(kServices));
  std::copy(kServices, kServices + arraysize(kServices), names->begin());
}

void TokenService::FireCredentialsUpdatedNotification(
    const std::string& lsid,
    const std::string& sid) {
  CredentialsUpdatedDetails details(lsid, sid);
  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED,
      content::Source<TokenService>(this),
      content::Details<const CredentialsUpdatedDetails>(&details));
}

// Note that this can fire twice or more for any given service.
// It can fire once from the DB read, and then once from the initial
// fetcher. Future fetches can cause more notification firings.
// The DB read will not however fire a notification if the fetcher
// returned first. So it's always safe to use the latest notification.
void TokenService::FireTokenAvailableNotification(
    const std::string& service,
    const std::string& auth_token) {

  TokenAvailableDetails details(service, auth_token);
  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_TOKEN_AVAILABLE,
      content::Source<TokenService>(this),
      content::Details<const TokenAvailableDetails>(&details));
}

void TokenService::FireTokenRequestFailedNotification(
    const std::string& service,
    const GoogleServiceAuthError& error) {

  TokenRequestFailedDetails details(service, error);
  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_TOKEN_REQUEST_FAILED,
      content::Source<TokenService>(this),
      content::Details<const TokenRequestFailedDetails>(&details));
}

void TokenService::IssueAuthTokenForTest(const std::string& service,
                                         const std::string& auth_token) {
  token_map_[service] = auth_token;
  FireTokenAvailableNotification(service, auth_token);
}

void TokenService::OnIssueAuthTokenSuccess(const std::string& service,
                                           const std::string& auth_token) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  VLOG(1) << "Got an authorization token for " << service;
  token_map_[service] = auth_token;
  FireTokenAvailableNotification(service, auth_token);
  SaveAuthTokenToDB(service, auth_token);
  // If we got ClientLogin token for "lso" service, then start fetching OAuth2
  // login scoped token pair.
  if (service == GaiaConstants::kLSOService) {
    int index = GetServiceIndex(service);
    CHECK_GE(index, 0);
    fetchers_[index]->StartLsoForOAuthLoginTokenExchange(auth_token);
  }
}

void TokenService::OnIssueAuthTokenFailure(const std::string& service,
    const GoogleServiceAuthError& error) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  LOG(WARNING) << "Auth token issuing failed for service:" << service
               << ", error: " << error.ToString();
  FireTokenRequestFailedNotification(service, error);
}

void TokenService::OnClientOAuthSuccess(const ClientOAuthResult& result) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  VLOG(1) << "Got OAuth2 login token pair";
  token_map_[GaiaConstants::kGaiaOAuth2LoginRefreshToken] =
      result.refresh_token;
  token_map_[GaiaConstants::kGaiaOAuth2LoginAccessToken] = result.access_token;
  SaveAuthTokenToDB(GaiaConstants::kGaiaOAuth2LoginRefreshToken,
      result.refresh_token);
  SaveAuthTokenToDB(GaiaConstants::kGaiaOAuth2LoginAccessToken,
      result.access_token);
  // We don't save expiration information for now.

  FireTokenAvailableNotification(GaiaConstants::kGaiaOAuth2LoginRefreshToken,
      result.refresh_token);
}

void TokenService::OnClientOAuthFailure(
   const GoogleServiceAuthError& error) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  LOG(WARNING) << "OAuth2 login token pair fetch failed: " << error.ToString();
  FireTokenRequestFailedNotification(
      GaiaConstants::kGaiaOAuth2LoginRefreshToken, error);
}

void TokenService::OnWebDataServiceRequestDone(WebDataService::Handle h,
                                               const WDTypedResult* result) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(token_loading_query_);
  token_loading_query_ = 0;

  // If the fetch failed, there will be no result. In that case, we just don't
  // load any tokens at all from the DB.
  if (result) {
    DCHECK(result->GetType() == TOKEN_RESULT);
    const WDResult<std::map<std::string, std::string> > * token_result =
        static_cast<const WDResult<std::map<std::string, std::string> > * > (
            result);
    LoadTokensIntoMemory(token_result->GetValue(), &token_map_);
  }

  tokens_loaded_ = true;
  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_TOKEN_LOADING_FINISHED,
      content::Source<TokenService>(this),
      content::NotificationService::NoDetails());
}

// Load tokens from the db_token map into the in memory token map.
void TokenService::LoadTokensIntoMemory(
    const std::map<std::string, std::string>& db_tokens,
    std::map<std::string, std::string>* in_memory_tokens) {

  for (size_t i = 0; i < arraysize(kServices); i++) {
    LoadSingleTokenIntoMemory(db_tokens, in_memory_tokens, kServices[i]);
  }
  LoadSingleTokenIntoMemory(db_tokens, in_memory_tokens,
      GaiaConstants::kGaiaOAuth2LoginRefreshToken);
  LoadSingleTokenIntoMemory(db_tokens, in_memory_tokens,
      GaiaConstants::kGaiaOAuth2LoginAccessToken);

  if (credentials_.lsid.empty() && credentials_.sid.empty()) {
    // Look for GAIA SID and LSID tokens.  If we have both, and the current
    // crendentials are empty, update the credentials.
    std::string lsid;
    std::string sid;

    if (db_tokens.count(GaiaConstants::kGaiaLsid) > 0)
      lsid = db_tokens.find(GaiaConstants::kGaiaLsid)->second;

    if (db_tokens.count(GaiaConstants::kGaiaSid) > 0)
      sid = db_tokens.find(GaiaConstants::kGaiaSid)->second;

    if (!lsid.empty() && !sid.empty()) {
      UpdateCredentials(GaiaAuthConsumer::ClientLoginResult(sid,
                                                            lsid,
                                                            std::string(),
                                                            std::string()));
    }
  }
}

void TokenService::LoadSingleTokenIntoMemory(
    const std::map<std::string, std::string>& db_tokens,
    std::map<std::string, std::string>* in_memory_tokens,
    const std::string& service) {
  // OnIssueAuthTokenSuccess should come from the same thread.
  // If a token is already present in the map, it could only have
  // come from a DB read or from IssueAuthToken. Since we should never
  // fetch from the DB twice in a browser session, it must be from
  // OnIssueAuthTokenSuccess, which is a live fetcher.
  //
  // Network fetched tokens take priority over DB tokens, so exclude tokens
  // which have already been loaded by the fetcher.
  if (!in_memory_tokens->count(service) && db_tokens.count(service)) {
    std::string db_token = db_tokens.find(service)->second;
    if (!db_token.empty()) {
      VLOG(1) << "Loading " << service << " token from DB: " << db_token;
      (*in_memory_tokens)[service] = db_token;
      FireTokenAvailableNotification(service, db_token);
      // Failures are only for network errors.
    }
  }
}

void TokenService::Observe(int type,
                           const content::NotificationSource& source,
                           const content::NotificationDetails& details) {
  DCHECK_EQ(type, chrome::NOTIFICATION_TOKEN_UPDATED);
  TokenAvailableDetails* tok_details =
      content::Details<TokenAvailableDetails>(details).ptr();
  OnIssueAuthTokenSuccess(tok_details->service(), tok_details->token());
}
<commit_msg>[Sync] Avoid unnecessary DB rewrite for TokenService credentials that were just read from DB<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/signin/token_service.h"

#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/string_util.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/webdata/web_data_service_factory.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/net/gaia/gaia_auth_fetcher.h"
#include "chrome/common/net/gaia/gaia_constants.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "net/url_request/url_request_context_getter.h"

using content::BrowserThread;

namespace {

// List of services that are capable of ClientLogin-based authentication.
const char* kServices[] = {
  GaiaConstants::kGaiaService,
  GaiaConstants::kSyncService,
  GaiaConstants::kDeviceManagementService,
  GaiaConstants::kLSOService
};

}  // namespace

TokenService::TokenService()
    : profile_(NULL),
      token_loading_query_(0),
      tokens_loaded_(false) {
  // Allow constructor to be called outside the UI thread, so it can be mocked
  // out for unit tests.

  COMPILE_ASSERT(arraysize(kServices) == arraysize(fetchers_),
                 kServices_and_fetchers_dont_have_same_size);
}

TokenService::~TokenService() {
  if (!source_.empty()) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    ResetCredentialsInMemory();
  }
}

void TokenService::Initialize(const char* const source,
                              Profile* profile) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (!source_.empty()) {
    // Already initialized.
    return;
  }
  DCHECK(!profile_);
  profile_ = profile;
  getter_ = profile->GetRequestContext();
  // Since the user can create a bookmark in incognito, sync may be running.
  // Thus we have to go for explicit access.
  web_data_service_ = WebDataServiceFactory::GetForProfile(
      profile, Profile::EXPLICIT_ACCESS);
  source_ = std::string(source);

  CommandLine* cmd_line = CommandLine::ForCurrentProcess();
  // Allow the token service to be cleared from the command line. We rely on
  // SigninManager::Initialize() being called to clear out the
  // kGoogleServicesUsername pref before we call EraseTokensFromDB() as
  // otherwise the system would be in an invalid state.
  if (cmd_line->HasSwitch(switches::kClearTokenService))
    EraseTokensFromDB();

  // Allow a token to be injected from the command line.
  if (cmd_line->HasSwitch(switches::kSetToken)) {
    std::string value = cmd_line->GetSwitchValueASCII(switches::kSetToken);
    int separator = value.find(':');
    std::string service = value.substr(0, separator);
    std::string token = value.substr(separator + 1);
    token_map_[service] = token;
    SaveAuthTokenToDB(service, token);
  }

  registrar_.Add(this,
                 chrome::NOTIFICATION_TOKEN_UPDATED,
                 content::Source<Profile>(profile));
}

void TokenService::ResetCredentialsInMemory() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  // Terminate any running fetchers. Callbacks will not return.
  for (size_t i = 0; i < arraysize(kServices); ++i) {
    fetchers_[i].reset();
  }

  // Cancel pending loads. Callbacks will not return.
  if (token_loading_query_) {
    web_data_service_->CancelRequest(token_loading_query_);
    token_loading_query_ = 0;
  }

  tokens_loaded_ = false;
  token_map_.clear();
  credentials_ = GaiaAuthConsumer::ClientLoginResult();
}

void TokenService::UpdateCredentials(
    const GaiaAuthConsumer::ClientLoginResult& credentials) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  credentials_ = credentials;

  SaveAuthTokenToDB(GaiaConstants::kGaiaLsid, credentials.lsid);
  SaveAuthTokenToDB(GaiaConstants::kGaiaSid, credentials.sid);

  // Cancel any currently running requests.
  for (size_t i = 0; i < arraysize(kServices); i++) {
    fetchers_[i].reset();
  }

  // Notify the CredentialCacheService that a new lsid and sid are available.
  FireCredentialsUpdatedNotification(credentials.lsid, credentials.sid);
}

void TokenService::UpdateCredentialsWithOAuth2(
    const GaiaAuthConsumer::ClientOAuthResult& credentials) {
  // Will be implemented once the ClientOAuth signin is complete.  Not called
  // yet by any code.
  NOTREACHED();
}

void TokenService::LoadTokensFromDB() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (web_data_service_.get())
    token_loading_query_ = web_data_service_->GetAllTokens(this);
}

void TokenService::SaveAuthTokenToDB(const std::string& service,
                                     const std::string& auth_token) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (web_data_service_.get())
    web_data_service_->SetTokenForService(service, auth_token);
}

void TokenService::EraseTokensFromDB() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  // Try to track down http://crbug.com/121755 - we should never clear the
  // token DB while we're still logged in.
  if (profile_) {
    std::string user = profile_->GetPrefs()->GetString(
        prefs::kGoogleServicesUsername);
    CHECK(user.empty());
  }
  if (web_data_service_.get())
    web_data_service_->RemoveAllTokens();

  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_TOKENS_CLEARED,
      content::Source<TokenService>(this),
      content::NotificationService::NoDetails());
}

bool TokenService::TokensLoadedFromDB() const {
  return tokens_loaded_;
}

// static
int TokenService::GetServiceIndex(const std::string& service) {
  for (size_t i = 0; i < arraysize(kServices); ++i) {
    if (kServices[i] == service)
      return i;
  }
  return -1;
}

bool TokenService::AreCredentialsValid() const {
  return !credentials_.lsid.empty() && !credentials_.sid.empty();
}

void TokenService::StartFetchingTokens() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(AreCredentialsValid());
  for (size_t i = 0; i < arraysize(kServices); i++) {
    fetchers_[i].reset(new GaiaAuthFetcher(this, source_, getter_));
    fetchers_[i]->StartIssueAuthToken(credentials_.sid,
                                      credentials_.lsid,
                                      kServices[i]);
  }
}

// Services dependent on a token will check if a token is available.
// If it isn't, they'll go to sleep until they get a token event.
bool TokenService::HasTokenForService(const char* service) const {
  return token_map_.count(service) > 0;
}

const std::string& TokenService::GetTokenForService(
    const char* const service) const {

  if (token_map_.count(service) > 0) {
    // Note map[key] is not const.
    return (*token_map_.find(service)).second;
  }
  return EmptyString();
}

bool TokenService::HasOAuthLoginToken() const {
  return HasTokenForService(GaiaConstants::kGaiaOAuth2LoginRefreshToken);
}

const std::string& TokenService::GetOAuth2LoginRefreshToken() const {
  return GetTokenForService(GaiaConstants::kGaiaOAuth2LoginRefreshToken);
}

const std::string& TokenService::GetOAuth2LoginAccessToken() const {
  return GetTokenForService(GaiaConstants::kGaiaOAuth2LoginAccessToken);
}

// static
void TokenService::GetServiceNamesForTesting(std::vector<std::string>* names) {
  names->resize(arraysize(kServices));
  std::copy(kServices, kServices + arraysize(kServices), names->begin());
}

void TokenService::FireCredentialsUpdatedNotification(
    const std::string& lsid,
    const std::string& sid) {
  CredentialsUpdatedDetails details(lsid, sid);
  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_TOKEN_SERVICE_CREDENTIALS_UPDATED,
      content::Source<TokenService>(this),
      content::Details<const CredentialsUpdatedDetails>(&details));
}

// Note that this can fire twice or more for any given service.
// It can fire once from the DB read, and then once from the initial
// fetcher. Future fetches can cause more notification firings.
// The DB read will not however fire a notification if the fetcher
// returned first. So it's always safe to use the latest notification.
void TokenService::FireTokenAvailableNotification(
    const std::string& service,
    const std::string& auth_token) {

  TokenAvailableDetails details(service, auth_token);
  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_TOKEN_AVAILABLE,
      content::Source<TokenService>(this),
      content::Details<const TokenAvailableDetails>(&details));
}

void TokenService::FireTokenRequestFailedNotification(
    const std::string& service,
    const GoogleServiceAuthError& error) {

  TokenRequestFailedDetails details(service, error);
  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_TOKEN_REQUEST_FAILED,
      content::Source<TokenService>(this),
      content::Details<const TokenRequestFailedDetails>(&details));
}

void TokenService::IssueAuthTokenForTest(const std::string& service,
                                         const std::string& auth_token) {
  token_map_[service] = auth_token;
  FireTokenAvailableNotification(service, auth_token);
}

void TokenService::OnIssueAuthTokenSuccess(const std::string& service,
                                           const std::string& auth_token) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  VLOG(1) << "Got an authorization token for " << service;
  token_map_[service] = auth_token;
  FireTokenAvailableNotification(service, auth_token);
  SaveAuthTokenToDB(service, auth_token);
  // If we got ClientLogin token for "lso" service, then start fetching OAuth2
  // login scoped token pair.
  if (service == GaiaConstants::kLSOService) {
    int index = GetServiceIndex(service);
    CHECK_GE(index, 0);
    fetchers_[index]->StartLsoForOAuthLoginTokenExchange(auth_token);
  }
}

void TokenService::OnIssueAuthTokenFailure(const std::string& service,
    const GoogleServiceAuthError& error) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  LOG(WARNING) << "Auth token issuing failed for service:" << service
               << ", error: " << error.ToString();
  FireTokenRequestFailedNotification(service, error);
}

void TokenService::OnClientOAuthSuccess(const ClientOAuthResult& result) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  VLOG(1) << "Got OAuth2 login token pair";
  token_map_[GaiaConstants::kGaiaOAuth2LoginRefreshToken] =
      result.refresh_token;
  token_map_[GaiaConstants::kGaiaOAuth2LoginAccessToken] = result.access_token;
  SaveAuthTokenToDB(GaiaConstants::kGaiaOAuth2LoginRefreshToken,
      result.refresh_token);
  SaveAuthTokenToDB(GaiaConstants::kGaiaOAuth2LoginAccessToken,
      result.access_token);
  // We don't save expiration information for now.

  FireTokenAvailableNotification(GaiaConstants::kGaiaOAuth2LoginRefreshToken,
      result.refresh_token);
}

void TokenService::OnClientOAuthFailure(
   const GoogleServiceAuthError& error) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  LOG(WARNING) << "OAuth2 login token pair fetch failed: " << error.ToString();
  FireTokenRequestFailedNotification(
      GaiaConstants::kGaiaOAuth2LoginRefreshToken, error);
}

void TokenService::OnWebDataServiceRequestDone(WebDataService::Handle h,
                                               const WDTypedResult* result) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(token_loading_query_);
  token_loading_query_ = 0;

  // If the fetch failed, there will be no result. In that case, we just don't
  // load any tokens at all from the DB.
  if (result) {
    DCHECK(result->GetType() == TOKEN_RESULT);
    const WDResult<std::map<std::string, std::string> > * token_result =
        static_cast<const WDResult<std::map<std::string, std::string> > * > (
            result);
    LoadTokensIntoMemory(token_result->GetValue(), &token_map_);
  }

  tokens_loaded_ = true;
  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_TOKEN_LOADING_FINISHED,
      content::Source<TokenService>(this),
      content::NotificationService::NoDetails());
}

// Load tokens from the db_token map into the in memory token map.
void TokenService::LoadTokensIntoMemory(
    const std::map<std::string, std::string>& db_tokens,
    std::map<std::string, std::string>* in_memory_tokens) {
  // Ensure that there are no active fetchers at the time we first load
  // tokens from the DB into memory.
  for (size_t i = 0; i < arraysize(kServices); ++i) {
    DCHECK(NULL == fetchers_[i].get());
  }

  for (size_t i = 0; i < arraysize(kServices); i++) {
    LoadSingleTokenIntoMemory(db_tokens, in_memory_tokens, kServices[i]);
  }
  LoadSingleTokenIntoMemory(db_tokens, in_memory_tokens,
      GaiaConstants::kGaiaOAuth2LoginRefreshToken);
  LoadSingleTokenIntoMemory(db_tokens, in_memory_tokens,
      GaiaConstants::kGaiaOAuth2LoginAccessToken);

  if (credentials_.lsid.empty() && credentials_.sid.empty()) {
    // Look for GAIA SID and LSID tokens.  If we have both, and the current
    // crendentials are empty, update the credentials.
    std::string lsid;
    std::string sid;

    if (db_tokens.count(GaiaConstants::kGaiaLsid) > 0)
      lsid = db_tokens.find(GaiaConstants::kGaiaLsid)->second;

    if (db_tokens.count(GaiaConstants::kGaiaSid) > 0)
      sid = db_tokens.find(GaiaConstants::kGaiaSid)->second;

    if (!lsid.empty() && !sid.empty()) {
      credentials_ = GaiaAuthConsumer::ClientLoginResult(sid,
                                                         lsid,
                                                         std::string(),
                                                         std::string());
    }
  }
}

void TokenService::LoadSingleTokenIntoMemory(
    const std::map<std::string, std::string>& db_tokens,
    std::map<std::string, std::string>* in_memory_tokens,
    const std::string& service) {
  // OnIssueAuthTokenSuccess should come from the same thread.
  // If a token is already present in the map, it could only have
  // come from a DB read or from IssueAuthToken. Since we should never
  // fetch from the DB twice in a browser session, it must be from
  // OnIssueAuthTokenSuccess, which is a live fetcher.
  //
  // Network fetched tokens take priority over DB tokens, so exclude tokens
  // which have already been loaded by the fetcher.
  if (!in_memory_tokens->count(service) && db_tokens.count(service)) {
    std::string db_token = db_tokens.find(service)->second;
    if (!db_token.empty()) {
      VLOG(1) << "Loading " << service << " token from DB: " << db_token;
      (*in_memory_tokens)[service] = db_token;
      FireTokenAvailableNotification(service, db_token);
      // Failures are only for network errors.
    }
  }
}

void TokenService::Observe(int type,
                           const content::NotificationSource& source,
                           const content::NotificationDetails& details) {
  DCHECK_EQ(type, chrome::NOTIFICATION_TOKEN_UPDATED);
  TokenAvailableDetails* tok_details =
      content::Details<TokenAvailableDetails>(details).ptr();
  OnIssueAuthTokenSuccess(tok_details->service(), tok_details->token());
}
<|endoftext|>


<commit_before>#ifndef __ENVIRE_CORE_GRAPHVIZ__
#define __ENVIRE_CORE_GRAPHVIZ__

#include <fstream> // std::ofstream

#include <envire_core/TransformTree.hpp>
#include <boost/graph/graphviz.hpp>

namespace envire { namespace core
{

    /**@class Transform Writer
    * Frame Graph Viz property writer for boost graphs
    * */
    template <class _Frame>
    class FrameWriter
    {
    public:
        FrameWriter(_Frame _f):f(_f){}
        template <class _Vertex>
        void operator()(std::ostream &out, const _Vertex& n) const
        {
            out << "[shape=record, label=\"<f0> " << f[n].name <<
                "|<f1>" << f[n].items.size()<<"\""
                <<",style=filled,fillcolor=lightblue]";
        }

    private:
        _Frame f;

    };

    /**@class Transform Writer
     * Transform Graph Viz Property writer for boost graphs
     * */
    template <class _Transform>
    class TransformWriter
    {
    public:
        TransformWriter(_Transform _tf):tf(_tf){}
        template <class _Edge>
        void operator()(std::ostream &out, const _Edge& e) const
        {
            out << "[label=\"" << tf[e].time.toString(::base::Time::Seconds) <<
                boost::format("\\nt: (%.1f %.1f %.1f)\\nr: (%.1f %.1f %.1f)") % tf[e].transform.translation.x() % tf[e].transform.translation.y() % tf[e].transform.translation.z()
                % tf[e].transform.orientation.axis()[0] % tf[e].transform.orientation.axis()[1] % tf[e].transform.orientation.axis()[2]
                << "\""
                << ",shape=ellipse,color=red,style=filled,fillcolor=lightcoral]";
        }
    private:
        _Transform tf;
    };

    /**@class Environment Writer
     * Transform Graph Viz Property writer for boost graphs
     * */
    class GraphPropWriter
    {
    public:
        GraphPropWriter(){}
        void operator()(std::ostream &out) const
        {
            //out<< "graph[rankdir=LR,splines=ortho];\n";
            out<< "graph[size=\"88,136\", ranksep=3.0, nodesep=2.0, fontname=\"Helvetica\", fontsize=8];\n";
        }
    };

    /**@class GraphViz
     * Class to print TransformGraphs in Graph Viz
     * */
    class GraphViz
    {

    protected:

        inline GraphPropWriter
        make_graph_writer()
        {
            return GraphPropWriter();
        }

        /**@brief Writer for Frame Node
         */
        template <class _Frame>
        inline FrameWriter<_Frame>
        make_node_writer(_Frame frame)
        {
            return FrameWriter<_Frame>(frame);
        }

        /**@brief Writer for Frame Node
         */
        template <class _Transform>
        inline TransformWriter<_Transform>
        make_edge_writer(_Transform tf)
        {
            return TransformWriter<_Transform>(tf);
        }

    public:

        /**@brief Export to GraphViz
         *
         */
        void write(const TransformGraph &graph, const std::string& filename = "")
        {
            std::streambuf * buf;
            std::ofstream of;

            if(!filename.empty())
            {
                of.open(filename.c_str());
                buf = of.rdbuf();
            }
            else
            {
                buf = std::cout.rdbuf();
            }

            /** Print graph **/
            std::ostream out(buf);
            boost::write_graphviz (out, graph,
                    make_node_writer(boost::get(&FrameProperty::frame, graph)),
                    make_edge_writer(boost::get(&TransformProperty::transform, graph)),
                    make_graph_writer());
        }
    };
}}
#endif

<commit_msg>src: graphviz has different color for the camera label (for the msc filter)<commit_after>#ifndef __ENVIRE_CORE_GRAPHVIZ__
#define __ENVIRE_CORE_GRAPHVIZ__

#include <fstream> // std::ofstream

#include <envire_core/TransformTree.hpp>
#include <boost/graph/graphviz.hpp>

namespace envire { namespace core
{

    /**@class Transform Writer
    * Frame Graph Viz property writer for boost graphs
    * */
    template <class _Frame>
    class FrameWriter
    {
    public:
        FrameWriter(_Frame _f):f(_f){}
        template <class _Vertex>
        void operator()(std::ostream &out, const _Vertex& n) const
        {
            if(f[n].name.find("camera") != std::string::npos)
            {
                out << "[shape=record, label=\"<f0> " << f[n].name <<
                    "|<f1>" << f[n].items.size()<<"\""
                    <<",style=filled,fillcolor=orange]";

            }
            else
            {
                out << "[shape=record, label=\"<f0> " << f[n].name <<
                    "|<f1>" << f[n].items.size()<<"\""
                    <<",style=filled,fillcolor=lightblue]";
            }
        }

    private:
        _Frame f;

    };

    /**@class Transform Writer
     * Transform Graph Viz Property writer for boost graphs
     * */
    template <class _Transform>
    class TransformWriter
    {
    public:
        TransformWriter(_Transform _tf):tf(_tf){}
        template <class _Edge>
        void operator()(std::ostream &out, const _Edge& e) const
        {
            out << "[label=\"" << tf[e].time.toString(::base::Time::Seconds) <<
                boost::format("\\nt: (%.1f %.1f %.1f)\\nr: (%.1f %.1f %.1f)") % tf[e].transform.translation.x() % tf[e].transform.translation.y() % tf[e].transform.translation.z()
                % tf[e].transform.orientation.axis()[0] % tf[e].transform.orientation.axis()[1] % tf[e].transform.orientation.axis()[2]
                << "\""
                << ",shape=ellipse,color=red,style=filled,fillcolor=lightcoral]";
        }
    private:
        _Transform tf;
    };

    /**@class Environment Writer
     * Transform Graph Viz Property writer for boost graphs
     * */
    class GraphPropWriter
    {
    public:
        GraphPropWriter(){}
        void operator()(std::ostream &out) const
        {
            //out<< "graph[rankdir=LR,splines=ortho];\n";
            out<< "graph[size=\"88,136\", ranksep=3.0, nodesep=2.0, fontname=\"Helvetica\", fontsize=8];\n";
        }
    };

    /**@class GraphViz
     * Class to print TransformGraphs in Graph Viz
     * */
    class GraphViz
    {

    protected:

        inline GraphPropWriter
        make_graph_writer()
        {
            return GraphPropWriter();
        }

        /**@brief Writer for Frame Node
         */
        template <class _Frame>
        inline FrameWriter<_Frame>
        make_node_writer(_Frame frame)
        {
            return FrameWriter<_Frame>(frame);
        }

        /**@brief Writer for Frame Node
         */
        template <class _Transform>
        inline TransformWriter<_Transform>
        make_edge_writer(_Transform tf)
        {
            return TransformWriter<_Transform>(tf);
        }

    public:

        /**@brief Export to GraphViz
         *
         */
        void write(const TransformGraph &graph, const std::string& filename = "")
        {
            std::streambuf * buf;
            std::ofstream of;

            if(!filename.empty())
            {
                of.open(filename.c_str());
                buf = of.rdbuf();
            }
            else
            {
                buf = std::cout.rdbuf();
            }

            /** Print graph **/
            std::ostream out(buf);
            boost::write_graphviz (out, graph,
                    make_node_writer(boost::get(&FrameProperty::frame, graph)),
                    make_edge_writer(boost::get(&TransformProperty::transform, graph)),
                    make_graph_writer());
        }
    };
}}
#endif

<|endoftext|>


<commit_before>/* GG is a GUI for SDL and OpenGL.
   Copyright (C) 2003-2008 T. Zachary Laine

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public License
   as published by the Free Software Foundation; either version 2.1
   of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA

   If you do not wish to comply with the terms of the LGPL please
   contact the author as other terms are available for a fee.

   Zach Laine
   whatwasthataddress@gmail.com */

#include <GG/GroupBox.h>

#include <GG/GUI.h>
#include <GG/DrawUtil.h>
#include <GG/StyleFactory.h>
#include <GG/TextControl.h>

using namespace GG;

namespace {
    Y TopOfFrame(bool label, const boost::shared_ptr<Font>& font)
    { return label ? font->Lineskip() / 2 - 1 : Y0; }
}

////////////////////////////////////////////////
// GG::GroupBox
////////////////////////////////////////////////
// static(s)
const int GroupBox::FRAME_THICK = 2;
const int GroupBox::PIXEL_MARGIN = 4;

GroupBox::GroupBox(X x, Y y, X w, Y h, const std::string& label, const boost::shared_ptr<Font>& font,
                   Clr color, Clr text_color/* = CLR_BLACK*/, Clr interior/* = CLR_ZERO*/,
                   Flags<WndFlag> flags/* = Flags<WndFlag>()*/) :
    Wnd(x, y, w, h, flags),
    m_color(color),
    m_text_color(text_color),
    m_int_color(interior),
    m_font(font),
    m_label(label.empty() ? 0 : GUI::GetGUI()->GetStyleFactory()->NewTextControl(X0, -m_font->Lineskip(), X1, m_font->Lineskip(),
                                                                                label, m_font, m_text_color, FORMAT_LEFT | FORMAT_TOP)),
    m_set_client_corners_equal_to_box_corners(false)
{ AttachChild(m_label); }

Pt GroupBox::ClientUpperLeft() const
{
    Pt retval = UpperLeft();
    if (!m_set_client_corners_equal_to_box_corners)
        retval += Pt(X(FRAME_THICK + PIXEL_MARGIN), Y(FRAME_THICK + PIXEL_MARGIN) + TopOfFrame(m_label, m_font));
    return retval;
}

Pt GroupBox::ClientLowerRight() const
{
    Pt retval = LowerRight();
    if (!m_set_client_corners_equal_to_box_corners)
        retval -= Pt(X(FRAME_THICK + PIXEL_MARGIN), Y(FRAME_THICK + PIXEL_MARGIN));
    return retval;
}

void GroupBox::Render()
{
    Pt ul = UpperLeft(), lr = LowerRight() - Pt(X1, Y1);
    ul.y += TopOfFrame(m_label, m_font);
    Clr light = LightColor(m_color);
    Clr dark = DarkColor(m_color);
    const int GAP_FROM_TEXT = 2;
    int vertices[24] = {
        Value(ul.x) + FRAME_THICK + PIXEL_MARGIN - GAP_FROM_TEXT, Value(ul.y),
        Value(ul.x), Value(ul.y),
        Value(ul.x), Value(lr.y),
        Value(lr.x), Value(lr.y),
        Value(lr.x), Value(ul.y),
        vertices[0], Value(ul.y)
    };
    if (m_label) {
        vertices[0] = Value(m_label->TextUpperLeft().x - GAP_FROM_TEXT);
        vertices[10] = Value(m_label->TextLowerRight().x + GAP_FROM_TEXT);
    }

    for (std::size_t i = 0; i < 12; i += 2) {
        vertices[12 + i + 0] = vertices[i + 0] + 1;
        vertices[12 + i + 1] = vertices[i + 1] + 1;
    }
    --vertices[12];
    --vertices[22];

    glDisable(GL_TEXTURE_2D);

    glColor(light);
    glBegin(GL_LINE_STRIP);
    for (std::size_t i = 12; i < 24; i += 2) {
        glVertex2i(vertices[i + 0], vertices[i + 1]);
    }
    glEnd();

    glColor(dark);
    glBegin(GL_LINE_STRIP);
    for (std::size_t i = 0; i < 12; i += 2) {
        glVertex2i(vertices[i + 0], vertices[i + 1]);
    }
    glEnd();

    glColor(m_int_color);
    glBegin(GL_QUADS);
    glVertex2i(vertices[14] + 1, vertices[5] - 1);
    glVertex2i(vertices[14] + 1, vertices[13] + 1);
    glVertex2i(vertices[6] - 1,  vertices[13] + 1);
    glVertex2i(vertices[6] - 1,  vertices[5] - 1);
    glEnd();

    glEnable(GL_TEXTURE_2D);
}

void GroupBox::SetColor(Clr c)
{ m_color = c; }

void GroupBox::SetTextColor(Clr c)
{ m_text_color = c; }

void GroupBox::SetInteriorColor(Clr c)
{ m_int_color = c; }

void GroupBox::SetClientCornersEqualToBoxCorners(bool b)
{
    if (b != m_set_client_corners_equal_to_box_corners) {
        m_set_client_corners_equal_to_box_corners = b;
        if (m_label) {
            if (m_set_client_corners_equal_to_box_corners)
                m_label->MoveTo(Pt(X(FRAME_THICK + PIXEL_MARGIN), Y0));
            else
                m_label->MoveTo(Pt(X0, -m_font->Lineskip()));
        }
    }
}

void GroupBox::SetText(const std::string& str)
{
    delete m_label;

    if (!str.empty()) {
        m_label = GUI::GetGUI()->GetStyleFactory()->NewTextControl(X(FRAME_THICK + PIXEL_MARGIN), Y0,
                                                                   X1, m_font->Lineskip(),
                                                                   str, m_font, m_text_color);
    }

    if (m_set_client_corners_equal_to_box_corners && m_label)
        m_label->MoveTo(Pt(X(FRAME_THICK + PIXEL_MARGIN), Y0));
}
<commit_msg>Fixed broken GroupBox::SetTextColor().<commit_after>/* GG is a GUI for SDL and OpenGL.
   Copyright (C) 2003-2008 T. Zachary Laine

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public License
   as published by the Free Software Foundation; either version 2.1
   of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA

   If you do not wish to comply with the terms of the LGPL please
   contact the author as other terms are available for a fee.

   Zach Laine
   whatwasthataddress@gmail.com */

#include <GG/GroupBox.h>

#include <GG/GUI.h>
#include <GG/DrawUtil.h>
#include <GG/StyleFactory.h>
#include <GG/TextControl.h>

using namespace GG;

namespace {
    Y TopOfFrame(bool label, const boost::shared_ptr<Font>& font)
    { return label ? font->Lineskip() / 2 - 1 : Y0; }
}

////////////////////////////////////////////////
// GG::GroupBox
////////////////////////////////////////////////
// static(s)
const int GroupBox::FRAME_THICK = 2;
const int GroupBox::PIXEL_MARGIN = 4;

GroupBox::GroupBox(X x, Y y, X w, Y h, const std::string& label, const boost::shared_ptr<Font>& font,
                   Clr color, Clr text_color/* = CLR_BLACK*/, Clr interior/* = CLR_ZERO*/,
                   Flags<WndFlag> flags/* = Flags<WndFlag>()*/) :
    Wnd(x, y, w, h, flags),
    m_color(color),
    m_text_color(text_color),
    m_int_color(interior),
    m_font(font),
    m_label(label.empty() ? 0 : GUI::GetGUI()->GetStyleFactory()->NewTextControl(X0, -m_font->Lineskip(), X1, m_font->Lineskip(),
                                                                                label, m_font, m_text_color, FORMAT_LEFT | FORMAT_TOP)),
    m_set_client_corners_equal_to_box_corners(false)
{ AttachChild(m_label); }

Pt GroupBox::ClientUpperLeft() const
{
    Pt retval = UpperLeft();
    if (!m_set_client_corners_equal_to_box_corners)
        retval += Pt(X(FRAME_THICK + PIXEL_MARGIN), Y(FRAME_THICK + PIXEL_MARGIN) + TopOfFrame(m_label, m_font));
    return retval;
}

Pt GroupBox::ClientLowerRight() const
{
    Pt retval = LowerRight();
    if (!m_set_client_corners_equal_to_box_corners)
        retval -= Pt(X(FRAME_THICK + PIXEL_MARGIN), Y(FRAME_THICK + PIXEL_MARGIN));
    return retval;
}

void GroupBox::Render()
{
    Pt ul = UpperLeft(), lr = LowerRight() - Pt(X1, Y1);
    ul.y += TopOfFrame(m_label, m_font);
    Clr light = LightColor(m_color);
    Clr dark = DarkColor(m_color);
    const int GAP_FROM_TEXT = 2;
    int vertices[24] = {
        Value(ul.x) + FRAME_THICK + PIXEL_MARGIN - GAP_FROM_TEXT, Value(ul.y),
        Value(ul.x), Value(ul.y),
        Value(ul.x), Value(lr.y),
        Value(lr.x), Value(lr.y),
        Value(lr.x), Value(ul.y),
        vertices[0], Value(ul.y)
    };
    if (m_label) {
        vertices[0] = Value(m_label->TextUpperLeft().x - GAP_FROM_TEXT);
        vertices[10] = Value(m_label->TextLowerRight().x + GAP_FROM_TEXT);
    }

    for (std::size_t i = 0; i < 12; i += 2) {
        vertices[12 + i + 0] = vertices[i + 0] + 1;
        vertices[12 + i + 1] = vertices[i + 1] + 1;
    }
    --vertices[12];
    --vertices[22];

    glDisable(GL_TEXTURE_2D);

    glColor(light);
    glBegin(GL_LINE_STRIP);
    for (std::size_t i = 12; i < 24; i += 2) {
        glVertex2i(vertices[i + 0], vertices[i + 1]);
    }
    glEnd();

    glColor(dark);
    glBegin(GL_LINE_STRIP);
    for (std::size_t i = 0; i < 12; i += 2) {
        glVertex2i(vertices[i + 0], vertices[i + 1]);
    }
    glEnd();

    glColor(m_int_color);
    glBegin(GL_QUADS);
    glVertex2i(vertices[14] + 1, vertices[5] - 1);
    glVertex2i(vertices[14] + 1, vertices[13] + 1);
    glVertex2i(vertices[6] - 1,  vertices[13] + 1);
    glVertex2i(vertices[6] - 1,  vertices[5] - 1);
    glEnd();

    glEnable(GL_TEXTURE_2D);
}

void GroupBox::SetColor(Clr c)
{ m_color = c; }

void GroupBox::SetTextColor(Clr c)
{
    m_text_color = c;
    if (m_label)
        m_label->SetTextColor(c);
}

void GroupBox::SetInteriorColor(Clr c)
{ m_int_color = c; }

void GroupBox::SetClientCornersEqualToBoxCorners(bool b)
{
    if (b != m_set_client_corners_equal_to_box_corners) {
        m_set_client_corners_equal_to_box_corners = b;
        if (m_label) {
            if (m_set_client_corners_equal_to_box_corners)
                m_label->MoveTo(Pt(X(FRAME_THICK + PIXEL_MARGIN), Y0));
            else
                m_label->MoveTo(Pt(X0, -m_font->Lineskip()));
        }
    }
}

void GroupBox::SetText(const std::string& str)
{
    delete m_label;

    if (!str.empty()) {
        m_label = GUI::GetGUI()->GetStyleFactory()->NewTextControl(X(FRAME_THICK + PIXEL_MARGIN), Y0,
                                                                   X1, m_font->Lineskip(),
                                                                   str, m_font, m_text_color);
    }

    if (m_set_client_corners_equal_to_box_corners && m_label)
        m_label->MoveTo(Pt(X(FRAME_THICK + PIXEL_MARGIN), Y0));
}
<|endoftext|>