<commit_before>// Copyright 2014, Chris Pearce & Yun Sing Koh
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <time.h>
#include "utils.h"
#include "debug.h"
#include "Options.h"
#include <string>
#include <vector>
#include <stdarg.h>
#include <fstream>
#include "InvertedDataSetIndex.h"
#include "List.h"
#include "PatternStream.h"
#include "ItemMap.h"
#include <memory>

using namespace std;

/*
Usage:

 vector<string> tokens;
	 string str("Split,me up!Word2 Word3.");
	 Tokenize(str, tokens, ",<'> " );
	 vector <string>::iterator iter;
	 for(iter = tokens.begin();iter!=tokens.end();iter++){
	 		cout<< (*iter) << endl;
	}

*/

void Tokenize(const string& str,
              vector<string>& tokens,
              const string& delimiters) {
  // Skip delimiters at beginning.
  string::size_type lastPos = str.find_first_not_of(delimiters, 0);
  // Find first "non-delimiter".
  string::size_type pos     = str.find_first_of(delimiters, lastPos);
  while (string::npos != pos || string::npos != lastPos) {
    // Found a token, add it to the vector.
    tokens.push_back(str.substr(lastPos, pos - lastPos));
    // Skip delimiters.  Note the "not_of"
    lastPos = str.find_first_not_of(delimiters, pos);
    // Find next "non-delimiter"
    pos = str.find_first_of(delimiters, lastPos);
  }
}

// Trims leading and trailing whitespace.
string& TrimWhiteSpace(string& str) {
  char const* delims = " \t\r\n";

  // trim leading whitespace
  string::size_type notwhite = str.find_first_not_of(delims);
  str.erase(0, notwhite);

  // trim trailing whitespace
  notwhite = str.find_last_not_of(delims);
  str.erase(notwhite + 1);

  return str;
}

string TrimWhiteSpace(const string& str) {
  string s = str;
  return TrimWhiteSpace(s);
}

unsigned countUsingLoop(unsigned x) {
  unsigned r = 0;
  while (x != 0) {
    x = x & (x - 1);
    r++;
  }
  return r;
}

unsigned gBitCountLookupTable[256];

unsigned PopulationCount(unsigned x) {
  return
    gBitCountLookupTable[x & 0xFF] +
    gBitCountLookupTable[(x & 0xFF00) >> 8] +
    gBitCountLookupTable[(x & 0xFF0000) >> 16] +
    gBitCountLookupTable[(x & 0xFF000000) >> 24];
}

void InitializePopulationCount() {
  for (int i = 0; i < 256; i++) {
    gBitCountLookupTable[i] = countUsingLoop(i);
  }
}

static FILE* gOutputLog = 0;

bool InitLog(Options& options) {
  if (gOutputLog == 0) {
    string filename = GetOutputLogFileName(options.outputFilePrefix);
    gOutputLog = fopen(filename.c_str(), "w");
    if (!gOutputLog) {
      cerr << "Fail: Can't open log file '" << filename << "'\n";
      return false;
    }
  }
  return true;
}

void CloseLog() {
  fclose(gOutputLog);
}

void Log(const char* msg, ...) {
  va_list argp;
  va_start(argp, msg);
  vfprintf(stdout, msg, argp);
  if (gOutputLog) {
    vfprintf(gOutputLog, msg, argp);
  }
  va_end(argp);
}

bool DumpItemSets(vector<ItemSet>& aResult,
                  DataSet* aIndex,
                  Options& options) {
  if (options.countItemSetsOnly) {
    return true;
  }

  string filename = GetOutputItemsetsFileName(options.outputFilePrefix);
  ofstream out(filename.c_str());
  ASSERT(out);
  if (!out) {
    cerr << "Fail: Can't open '" << filename << "' for writing\n";
    return false;
  }
  for (unsigned i = 0; i < aResult.size(); i++) {
    string s = aResult[i];
    double sup = aIndex->Support(aResult[i]);
    if (sup < options.minSup) {
      cerr << "WARNING: itemset " << s << " has support of "
           << sup << ", but minsup=" << options.minSup << endl;
    }
    out << s << "," << sup << "\n";
  }
  out.close();
  return true;
}

double Confidence(Rule& r, DataSet* aIndex) {
  ItemSet AB(r.mAntecedent, r.mConsequent);
  return (double)aIndex->Support(AB) / (double)aIndex->Support(r.mAntecedent);
}

double Lift(Rule& r, DataSet* aIndex) {
  return Confidence(r, aIndex) / (double)aIndex->Support(r.mConsequent);
}

void GenerateRulesRecursize(long& aNumRules,
                            double minConf,
                            double minLift,
                            DataSet* aIndex,
                            set<Item>::iterator itr,
                            set<Item>::iterator end,
                            ItemSet& aAntecedent,
                            ItemSet& aConsequent,
                            bool aCountRulesOnly,
                            ostream& out) {
  if (itr == end) {
    if (aConsequent.Size() == 0 || aAntecedent.Size() == 0) {
      return;
    }

    // Calculate the confidence, lift, and support of the rule.
    double confidence = 0;
    double support = 0;
    double lift = 0;

    {
      ItemSet AB(aAntecedent, aConsequent);
      support = aIndex->Support(AB);
      confidence = support / (double)aIndex->Support(aAntecedent);
      lift = confidence / (double)aIndex->Support(aConsequent);
    }

    if (confidence > minConf &&
        lift > minLift) {
      aNumRules++;
      Rule r;
      r.mAntecedent = aAntecedent;
      r.mConsequent = aConsequent;
      string s = r;
      if (!aCountRulesOnly) {
        out << s << "," << confidence << "," << lift << "," << support* aIndex->NumTransactions() << endl;
      }
    }
    return;
  }

  Item item = *itr;
  itr++;

  aAntecedent.mItems.insert(item);
  GenerateRulesRecursize(aNumRules, minConf, minLift, aIndex, itr, end, aAntecedent, aConsequent, aCountRulesOnly, out);
  aAntecedent.mItems.erase(item);

  aConsequent.mItems.insert(item);
  GenerateRulesRecursize(aNumRules, minConf, minLift, aIndex, itr, end, aAntecedent, aConsequent, aCountRulesOnly, out);
  aConsequent.mItems.erase(item);
}

void GenerateRules(PatternInputStream& input,
                   double minConf,
                   double minLift,
                   long& aNumRules,
                   DataSet* aIndex,
                   string aOutputPrefix,
                   bool countRulesOnly) {
  time_t startTime = time(0);

  aNumRules = 0;

  string filename = GetOutputRuleFileName(aOutputPrefix);
  ofstream out(filename);
  ASSERT(out.is_open());

  ItemSet candidate;
  while (!(candidate = input.Read()).IsNull()) {
    ItemSet antecedent, consequent;
    GenerateRulesRecursize(aNumRules, minConf, minLift, aIndex,
                           candidate.mItems.begin(), candidate.mItems.end(),
                           antecedent, consequent, countRulesOnly, out);
  }

  time_t timeTaken = time(0) - startTime;
  Log("Generated %d rules in %lld seconds%s\n", aNumRules, timeTaken,
      (countRulesOnly ? " (not saved to disk)" : ""));
}

void GenerateRules(const vector<ItemSet>& aItemsets,
                   double minConf,
                   double minLift,
                   long& aNumRules,
                   DataSet* aIndex,
                   string aOutputPrefix,
                   bool countRulesOnly) {
  time_t startTime = time(0);

  aNumRules = 0;

  string filename = GetOutputRuleFileName(aOutputPrefix);
  ofstream out(filename);
  ASSERT(out.is_open());

  for (unsigned i = 0; i < aItemsets.size(); i++) {
    ItemSet candidate = aItemsets[i];
    ItemSet antecedent, consequent;
    GenerateRulesRecursize(aNumRules, minConf, minLift, aIndex,
                           candidate.mItems.begin(), candidate.mItems.end(),
                           antecedent, consequent, countRulesOnly, out);
  }

  time_t timeTaken = time(0) - startTime;
  Log("Generated %d rules in %lld seconds\n", aNumRules, timeTaken);
}

void List_Test() {
  List<int> list;
  for (int i = 0; i <= 20; i++) {
    list.Append(i);
  }
  ASSERT(list.GetSize() == 21);

  {
    vector<int> L;
    List<int>::Iterator* itr = list.Begin();
    while (itr->HasNext()) {
      L.push_back(itr->Next());
    }
    for (int i = 0; i <= 20; i++) {
      ASSERT(L[i] == i);
    }
    delete itr;
  }

  List<int>::Iterator* itr = list.Begin();
  int expected = 0;
  while (itr->HasNext()) {
    List<int>::Token token = itr->GetToken();
    ASSERT(token.IsInList());
    int x = itr->Next();
    if (expected % 2 == 0) {
      list.Erase(token);
      ASSERT(!token.IsInList());
    }
    ASSERT(x == expected);
    expected++;
  }
  ASSERT(expected == 21);
  ASSERT(list.GetSize() == 10);
  ASSERT(!itr->HasNext());
  for (int i = 21; i <= 40; i++) {
    list.Append(i);
  }
  ASSERT(itr->HasNext());
  while (itr->HasNext()) {
    int x = itr->Next();
    ASSERT(x == expected);
    expected++;
  }
  ASSERT(!itr->HasNext());

  list.Prepend(-1);
  ASSERT(!itr->HasNext());
  itr = list.Begin();
  ASSERT(itr->Next() == -1);
  delete itr;
}

void ItemMap_Test() {
  ItemMap<unsigned> m;
  for (unsigned i = 1; i < 1000; ++i) {
    Item item(i);
    m.Set(item, i);
  }

  ItemMap<unsigned>::Iterator itr = m.GetIterator();
  unsigned expected = 1;
  while (itr.HasNext()) {
    Item item = itr.GetKey();
    unsigned value = itr.GetValue();
    ASSERT(item.GetId() == expected);
    ASSERT(value == expected);
    itr.Next();
    expected++;
  }
  ASSERT(expected == 1000);
}

// Converts a string in "a,b,c" form to a vector of items [a,b,c].
// Useful for testing.
std::vector<Item> ToItemVector(const std::string& _items) {
  std::vector<Item> items;
  std::vector<std::string> tokens;
  Tokenize(_items, tokens, ", ");
  items.reserve(tokens.size());
  for (const string& token : tokens) {
    items.push_back(Item(token));
  }
  return move(items);
}
<commit_msg>Make ItemMap_Test() not depend on the order of iteration.<commit_after>// Copyright 2014, Chris Pearce & Yun Sing Koh
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <time.h>
#include "utils.h"
#include "debug.h"
#include "Options.h"
#include <string>
#include <vector>
#include <stdarg.h>
#include <fstream>
#include "InvertedDataSetIndex.h"
#include "List.h"
#include "PatternStream.h"
#include "ItemMap.h"
#include <memory>

using namespace std;

/*
Usage:

 vector<string> tokens;
	 string str("Split,me up!Word2 Word3.");
	 Tokenize(str, tokens, ",<'> " );
	 vector <string>::iterator iter;
	 for(iter = tokens.begin();iter!=tokens.end();iter++){
	 		cout<< (*iter) << endl;
	}

*/

void Tokenize(const string& str,
              vector<string>& tokens,
              const string& delimiters) {
  // Skip delimiters at beginning.
  string::size_type lastPos = str.find_first_not_of(delimiters, 0);
  // Find first "non-delimiter".
  string::size_type pos     = str.find_first_of(delimiters, lastPos);
  while (string::npos != pos || string::npos != lastPos) {
    // Found a token, add it to the vector.
    tokens.push_back(str.substr(lastPos, pos - lastPos));
    // Skip delimiters.  Note the "not_of"
    lastPos = str.find_first_not_of(delimiters, pos);
    // Find next "non-delimiter"
    pos = str.find_first_of(delimiters, lastPos);
  }
}

// Trims leading and trailing whitespace.
string& TrimWhiteSpace(string& str) {
  char const* delims = " \t\r\n";

  // trim leading whitespace
  string::size_type notwhite = str.find_first_not_of(delims);
  str.erase(0, notwhite);

  // trim trailing whitespace
  notwhite = str.find_last_not_of(delims);
  str.erase(notwhite + 1);

  return str;
}

string TrimWhiteSpace(const string& str) {
  string s = str;
  return TrimWhiteSpace(s);
}

unsigned countUsingLoop(unsigned x) {
  unsigned r = 0;
  while (x != 0) {
    x = x & (x - 1);
    r++;
  }
  return r;
}

unsigned gBitCountLookupTable[256];

unsigned PopulationCount(unsigned x) {
  return
    gBitCountLookupTable[x & 0xFF] +
    gBitCountLookupTable[(x & 0xFF00) >> 8] +
    gBitCountLookupTable[(x & 0xFF0000) >> 16] +
    gBitCountLookupTable[(x & 0xFF000000) >> 24];
}

void InitializePopulationCount() {
  for (int i = 0; i < 256; i++) {
    gBitCountLookupTable[i] = countUsingLoop(i);
  }
}

static FILE* gOutputLog = 0;

bool InitLog(Options& options) {
  if (gOutputLog == 0) {
    string filename = GetOutputLogFileName(options.outputFilePrefix);
    gOutputLog = fopen(filename.c_str(), "w");
    if (!gOutputLog) {
      cerr << "Fail: Can't open log file '" << filename << "'\n";
      return false;
    }
  }
  return true;
}

void CloseLog() {
  fclose(gOutputLog);
}

void Log(const char* msg, ...) {
  va_list argp;
  va_start(argp, msg);
  vfprintf(stdout, msg, argp);
  if (gOutputLog) {
    vfprintf(gOutputLog, msg, argp);
  }
  va_end(argp);
}

bool DumpItemSets(vector<ItemSet>& aResult,
                  DataSet* aIndex,
                  Options& options) {
  if (options.countItemSetsOnly) {
    return true;
  }

  string filename = GetOutputItemsetsFileName(options.outputFilePrefix);
  ofstream out(filename.c_str());
  ASSERT(out);
  if (!out) {
    cerr << "Fail: Can't open '" << filename << "' for writing\n";
    return false;
  }
  for (unsigned i = 0; i < aResult.size(); i++) {
    string s = aResult[i];
    double sup = aIndex->Support(aResult[i]);
    if (sup < options.minSup) {
      cerr << "WARNING: itemset " << s << " has support of "
           << sup << ", but minsup=" << options.minSup << endl;
    }
    out << s << "," << sup << "\n";
  }
  out.close();
  return true;
}

double Confidence(Rule& r, DataSet* aIndex) {
  ItemSet AB(r.mAntecedent, r.mConsequent);
  return (double)aIndex->Support(AB) / (double)aIndex->Support(r.mAntecedent);
}

double Lift(Rule& r, DataSet* aIndex) {
  return Confidence(r, aIndex) / (double)aIndex->Support(r.mConsequent);
}

void GenerateRulesRecursize(long& aNumRules,
                            double minConf,
                            double minLift,
                            DataSet* aIndex,
                            set<Item>::iterator itr,
                            set<Item>::iterator end,
                            ItemSet& aAntecedent,
                            ItemSet& aConsequent,
                            bool aCountRulesOnly,
                            ostream& out) {
  if (itr == end) {
    if (aConsequent.Size() == 0 || aAntecedent.Size() == 0) {
      return;
    }

    // Calculate the confidence, lift, and support of the rule.
    double confidence = 0;
    double support = 0;
    double lift = 0;

    {
      ItemSet AB(aAntecedent, aConsequent);
      support = aIndex->Support(AB);
      confidence = support / (double)aIndex->Support(aAntecedent);
      lift = confidence / (double)aIndex->Support(aConsequent);
    }

    if (confidence > minConf &&
        lift > minLift) {
      aNumRules++;
      Rule r;
      r.mAntecedent = aAntecedent;
      r.mConsequent = aConsequent;
      string s = r;
      if (!aCountRulesOnly) {
        out << s << "," << confidence << "," << lift << "," << support* aIndex->NumTransactions() << endl;
      }
    }
    return;
  }

  Item item = *itr;
  itr++;

  aAntecedent.mItems.insert(item);
  GenerateRulesRecursize(aNumRules, minConf, minLift, aIndex, itr, end, aAntecedent, aConsequent, aCountRulesOnly, out);
  aAntecedent.mItems.erase(item);

  aConsequent.mItems.insert(item);
  GenerateRulesRecursize(aNumRules, minConf, minLift, aIndex, itr, end, aAntecedent, aConsequent, aCountRulesOnly, out);
  aConsequent.mItems.erase(item);
}

void GenerateRules(PatternInputStream& input,
                   double minConf,
                   double minLift,
                   long& aNumRules,
                   DataSet* aIndex,
                   string aOutputPrefix,
                   bool countRulesOnly) {
  time_t startTime = time(0);

  aNumRules = 0;

  string filename = GetOutputRuleFileName(aOutputPrefix);
  ofstream out(filename);
  ASSERT(out.is_open());

  ItemSet candidate;
  while (!(candidate = input.Read()).IsNull()) {
    ItemSet antecedent, consequent;
    GenerateRulesRecursize(aNumRules, minConf, minLift, aIndex,
                           candidate.mItems.begin(), candidate.mItems.end(),
                           antecedent, consequent, countRulesOnly, out);
  }

  time_t timeTaken = time(0) - startTime;
  Log("Generated %d rules in %lld seconds%s\n", aNumRules, timeTaken,
      (countRulesOnly ? " (not saved to disk)" : ""));
}

void GenerateRules(const vector<ItemSet>& aItemsets,
                   double minConf,
                   double minLift,
                   long& aNumRules,
                   DataSet* aIndex,
                   string aOutputPrefix,
                   bool countRulesOnly) {
  time_t startTime = time(0);

  aNumRules = 0;

  string filename = GetOutputRuleFileName(aOutputPrefix);
  ofstream out(filename);
  ASSERT(out.is_open());

  for (unsigned i = 0; i < aItemsets.size(); i++) {
    ItemSet candidate = aItemsets[i];
    ItemSet antecedent, consequent;
    GenerateRulesRecursize(aNumRules, minConf, minLift, aIndex,
                           candidate.mItems.begin(), candidate.mItems.end(),
                           antecedent, consequent, countRulesOnly, out);
  }

  time_t timeTaken = time(0) - startTime;
  Log("Generated %d rules in %lld seconds\n", aNumRules, timeTaken);
}

void List_Test() {
  List<int> list;
  for (int i = 0; i <= 20; i++) {
    list.Append(i);
  }
  ASSERT(list.GetSize() == 21);

  {
    vector<int> L;
    List<int>::Iterator* itr = list.Begin();
    while (itr->HasNext()) {
      L.push_back(itr->Next());
    }
    for (int i = 0; i <= 20; i++) {
      ASSERT(L[i] == i);
    }
    delete itr;
  }

  List<int>::Iterator* itr = list.Begin();
  int expected = 0;
  while (itr->HasNext()) {
    List<int>::Token token = itr->GetToken();
    ASSERT(token.IsInList());
    int x = itr->Next();
    if (expected % 2 == 0) {
      list.Erase(token);
      ASSERT(!token.IsInList());
    }
    ASSERT(x == expected);
    expected++;
  }
  ASSERT(expected == 21);
  ASSERT(list.GetSize() == 10);
  ASSERT(!itr->HasNext());
  for (int i = 21; i <= 40; i++) {
    list.Append(i);
  }
  ASSERT(itr->HasNext());
  while (itr->HasNext()) {
    int x = itr->Next();
    ASSERT(x == expected);
    expected++;
  }
  ASSERT(!itr->HasNext());

  list.Prepend(-1);
  ASSERT(!itr->HasNext());
  itr = list.Begin();
  ASSERT(itr->Next() == -1);
  delete itr;
}

void ItemMap_Test() {
  std::map<int, unsigned> expected;
  ItemMap<unsigned> m;
  for (unsigned i = 1; i < 1000; ++i) {
    Item item(i);
    m.Set(item, i);
    expected[item.GetId()] = i;
  }

  ItemMap<unsigned>::Iterator itr = m.GetIterator();
  int count = 0;
  while (itr.HasNext()) {
    Item item = itr.GetKey();
    unsigned value = itr.GetValue();
    ASSERT(value == expected[item.GetId()]);
    itr.Next();
    count++;
  }
  ASSERT(count == expected.size());
}

// Converts a string in "a,b,c" form to a vector of items [a,b,c].
// Useful for testing.
std::vector<Item> ToItemVector(const std::string& _items) {
  std::vector<Item> items;
  std::vector<std::string> tokens;
  Tokenize(_items, tokens, ", ");
  items.reserve(tokens.size());
  for (const string& token : tokens) {
    items.push_back(Item(token));
  }
  return move(items);
}
<|endoftext|>


<commit_before>
/* Callable objects that encapsulate an object's method. These are almost certainly duplicates
of something in Boost... */

template<class obj_t, class ret_t>
struct no_arg_method_caller_t {
    obj_t *obj;
    ret_t (obj_t::*m)();
    no_arg_method_caller_t(obj_t *o, ret_t (obj_t::*m)()) : obj(o), m(m) { }
    ret_t operator()() { return (obj->*m)(); }
};

template<class obj_t, class arg1_t, class ret_t>
struct one_arg_method_caller_t {
    obj_t *obj;
    ret_t (obj_t::*m)(arg1_t);
    arg1_t arg1;
    one_arg_method_caller_t(obj_t *o, ret_t (obj_t::*m)(arg1_t), arg1_t a1) : obj(o), m(m), arg1(a1) { }
    ret_t operator()() { return (obj->*m)(arg1); }
};

template<class obj_t, class arg1_t, class arg2_t, class ret_t>
struct two_arg_method_caller_t {
    obj_t *obj;
    ret_t (obj_t::*m)(arg1_t, arg2_t);
    arg1_t arg1;
    arg2_t arg2;
    two_arg_method_caller_t(obj_t *o, ret_t (obj_t::*m)(arg1_t, arg2_t), arg1_t a1, arg2_t a2)
        : obj(o), m(m), arg1(a1), arg2(a2) { }
    ret_t operator()() { return (obj->*m)(arg1, arg2); }
};

template<class obj_t, class arg1_t, class arg2_t, class arg3_t, class arg4_t, class ret_t>
struct four_arg_method_caller_t {
    obj_t *obj;
    ret_t (obj_t::*m)(arg1_t, arg2_t, arg3_t, arg4_t);
    arg1_t arg1;
    arg2_t arg2;
    arg3_t arg3;
    arg4_t arg4;
    four_arg_method_caller_t(obj_t *o, ret_t(obj_t::*m)(arg1_t, arg2_t, arg3_t, arg4_t), arg1_t a1, arg2_t a2, arg3_t a3, arg4_t a4)
        : obj(o), m(m), arg1(a1), arg2(a2), arg3(a3), arg4(a4) { }
    ret_t operator()() { return (obj->*m)(arg1, arg2, arg3, arg4); }
};

/* Functions to do something on another core in a way that is more convenient than
continue_on_thread() is. */

template<class callable_t>
struct thread_doer_t :
    public thread_message_t,
    public home_thread_mixin_t
{
    callable_t callable;
    int thread;
    enum state_t {
        state_go_to_core,
        state_go_home
    } state;

    thread_doer_t(const callable_t &callable, int thread)
        : callable(callable), thread(thread), state(state_go_to_core) { }

    void run() {
        state = state_go_to_core;
        if (continue_on_thread(thread, this)) {
            do_perform_job();
        }
    }

    void do_perform_job() {
        rassert(thread == get_thread_id());
        callable();
        do_return_home();
    }

    void do_return_home() {
        state = state_go_home;
        if (continue_on_thread(home_thread, this)) delete this;
    }

    void on_thread_switch() {
        switch (state) {
            case state_go_to_core:
                do_perform_job();
                return;
            case state_go_home:
                delete this;
                return;
            default:
                unreachable("Bad state.");
        }
    }
};

template<class callable_t>
void do_on_thread(int thread, const callable_t &callable) {
    thread_doer_t<callable_t> *fsm = new thread_doer_t<callable_t>(callable, thread);
    fsm->run();
}
template<class obj_t>
void do_on_thread(int thread, obj_t *obj, void (obj_t::*on_other_core)()) {
    do_on_thread(thread, no_arg_method_caller_t<obj_t, void>(obj, on_other_core));
}
template<class obj_t, class arg1_t>
void do_on_thread(int thread, obj_t *obj, void (obj_t::*on_other_core)(arg1_t), arg1_t arg1) {
    do_on_thread(thread, one_arg_method_caller_t<obj_t, arg1_t, void>(obj, on_other_core, arg1));
}
template<class obj_t, class arg1_t, class arg2_t>
void do_on_thread(int thread, obj_t *obj, void (obj_t::*on_other_core)(arg1_t, arg2_t), arg1_t arg1, arg2_t arg2) {
    do_on_thread(thread, two_arg_method_caller_t<obj_t, arg1_t, arg2_t, void>(obj, on_other_core, arg1, arg2));
}
template<class obj_t, class arg1_t, class arg2_t, class arg3_t, class arg4_t>
void do_on_thread(int thread, obj_t *obj, void (obj_t::*on_other_core)(arg1_t, arg2_t, arg3_t, arg4_t), arg1_t arg1, arg2_t arg2, arg3_t arg3, arg4_t arg4) {
    do_on_thread(thread, four_arg_method_caller_t<obj_t, arg1_t, arg2_t, arg3_t, arg4_t, void>(obj, on_other_core, arg1, arg2, arg3, arg4));
}

template<class callable_t>
struct later_doer_t :
    public thread_message_t
{
    callable_t callable;

    later_doer_t(const callable_t &callable)
        : callable(callable) {
        call_later_on_this_thread(this);
    }

    void on_thread_switch() {
        callable();
        delete this;
    }
};

template<class callable_t>
void do_later(const callable_t &callable) {
    new later_doer_t<callable_t>(callable);
}

template<class obj_t>
void do_later(obj_t *obj, void (obj_t::*later)()) {
    return do_later(no_arg_method_caller_t<obj_t, void>(obj, later));
}

template<class obj_t, class arg1_t>
void do_later(obj_t *obj, void (obj_t::*later)(arg1_t), arg1_t arg1) {
    return do_later(one_arg_method_caller_t<obj_t, arg1_t, void>(obj, later, arg1));
}
<commit_msg>Got rid of no_arg, one_arg, *method_caller_t in utils.tcc.<commit_after>#include <boost/bind.hpp>

/* Functions to do something on another core in a way that is more convenient than
continue_on_thread() is. */

template<class callable_t>
struct thread_doer_t :
    public thread_message_t,
    public home_thread_mixin_t
{
    callable_t callable;
    int thread;
    enum state_t {
        state_go_to_core,
        state_go_home
    } state;

    thread_doer_t(const callable_t &callable, int thread)
        : callable(callable), thread(thread), state(state_go_to_core) { }

    void run() {
        state = state_go_to_core;
        if (continue_on_thread(thread, this)) {
            do_perform_job();
        }
    }

    void do_perform_job() {
        rassert(thread == get_thread_id());
        callable();
        do_return_home();
    }

    void do_return_home() {
        state = state_go_home;
        if (continue_on_thread(home_thread, this)) delete this;
    }

    void on_thread_switch() {
        switch (state) {
            case state_go_to_core:
                do_perform_job();
                return;
            case state_go_home:
                delete this;
                return;
            default:
                unreachable("Bad state.");
        }
    }
};

template<class callable_t>
void do_on_thread(int thread, const callable_t &callable) {
    thread_doer_t<callable_t> *fsm = new thread_doer_t<callable_t>(callable, thread);
    fsm->run();
}
template<class obj_t>
void do_on_thread(int thread, obj_t *obj, void (obj_t::*on_other_core)()) {
    do_on_thread(thread, boost::bind(on_other_core, obj));
}
template<class obj_t, class arg1_t>
void do_on_thread(int thread, obj_t *obj, void (obj_t::*on_other_core)(arg1_t), arg1_t arg1) {
    do_on_thread(thread, boost::bind(on_other_core, obj, arg1));
}
template<class obj_t, class arg1_t, class arg2_t>
void do_on_thread(int thread, obj_t *obj, void (obj_t::*on_other_core)(arg1_t, arg2_t), arg1_t arg1, arg2_t arg2) {
    do_on_thread(thread, boost::bind(on_other_core, obj, arg1, arg2));
}
template<class obj_t, class arg1_t, class arg2_t, class arg3_t, class arg4_t>
void do_on_thread(int thread, obj_t *obj, void (obj_t::*on_other_core)(arg1_t, arg2_t, arg3_t, arg4_t), arg1_t arg1, arg2_t arg2, arg3_t arg3, arg4_t arg4) {
    do_on_thread(thread, boost::bind(on_other_core, obj, arg1, arg2, arg3, arg4));
}

template<class callable_t>
struct later_doer_t :
    public thread_message_t
{
    callable_t callable;

    later_doer_t(const callable_t &callable)
        : callable(callable) {
        call_later_on_this_thread(this);
    }

    void on_thread_switch() {
        callable();
        delete this;
    }
};

template<class callable_t>
void do_later(const callable_t &callable) {
    new later_doer_t<callable_t>(callable);
}

template<class obj_t>
void do_later(obj_t *obj, void (obj_t::*later)()) {
    return do_later(boost::bind(later, obj));
}

template<class obj_t, class arg1_t>
void do_later(obj_t *obj, void (obj_t::*later)(arg1_t), arg1_t arg1) {
    return do_later(boost::bind(later, obj, arg1));
}
<|endoftext|>


<commit_before>//===---- QueryParser.cpp - clang-query command parser --------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "QueryParser.h"
#include "Query.h"
#include "QuerySession.h"
#include "clang/ASTMatchers/Dynamic/Parser.h"
#include "clang/Basic/CharInfo.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include <set>

using namespace llvm;
using namespace clang::ast_matchers::dynamic;

namespace clang {
namespace query {

// Lex any amount of whitespace followed by a "word" (any sequence of
// non-whitespace characters) from the start of region [Begin,End).  If no word
// is found before End, return StringRef().  Begin is adjusted to exclude the
// lexed region.
StringRef QueryParser::lexWord() {
  while (true) {
    if (Begin == End)
      return StringRef(Begin, 0);

    if (!isWhitespace(*Begin))
      break;

    ++Begin;
  }

  const char *WordBegin = Begin;

  while (true) {
    ++Begin;

    if (Begin == End || isWhitespace(*Begin))
      return StringRef(WordBegin, Begin - WordBegin);
  }
}

// This is the StringSwitch-alike used by lexOrCompleteWord below. See that
// function for details.
template <typename T> struct QueryParser::LexOrCompleteWord {
  StringSwitch<T> Switch;

  QueryParser *P;
  StringRef Word;
  // Set to the completion point offset in Word, or StringRef::npos if
  // completion point not in Word.
  size_t WordCompletionPos;

  LexOrCompleteWord(QueryParser *P, StringRef Word, size_t WCP)
      : Switch(Word), P(P), Word(Word), WordCompletionPos(WCP) {}

  template <unsigned N>
  LexOrCompleteWord &Case(const char (&S)[N], const T &Value,
                          bool IsCompletion = true) {
    StringRef CaseStr(S, N - 1);

    if (WordCompletionPos == StringRef::npos)
      Switch.Case(S, Value);
    else if (N != 1 && IsCompletion && WordCompletionPos <= CaseStr.size() &&
             CaseStr.substr(0, WordCompletionPos) ==
                 Word.substr(0, WordCompletionPos))
      P->Completions.push_back(LineEditor::Completion(
          (CaseStr.substr(WordCompletionPos) + " ").str(), CaseStr));
    return *this;
  }

  T Default(const T& Value) const {
    return Switch.Default(Value);
  }
};

// Lexes a word and stores it in Word. Returns a LexOrCompleteWord<T> object
// that can be used like a llvm::StringSwitch<T>, but adds cases as possible
// completions if the lexed word contains the completion point.
template <typename T>
QueryParser::LexOrCompleteWord<T>
QueryParser::lexOrCompleteWord(StringRef &Word) {
  Word = lexWord();
  size_t WordCompletionPos = StringRef::npos;
  if (CompletionPos && CompletionPos <= Word.data() + Word.size()) {
    if (CompletionPos < Word.data())
      WordCompletionPos = 0;
    else
      WordCompletionPos = CompletionPos - Word.data();
  }
  return LexOrCompleteWord<T>(this, Word, WordCompletionPos);
}

QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) {
  StringRef ValStr;
  unsigned Value = lexOrCompleteWord<unsigned>(ValStr)
                      .Case("false", 0)
                      .Case("true", 1)
                      .Default(~0u);
  if (Value == ~0u) {
    return new InvalidQuery("expected 'true' or 'false', got '" + ValStr + "'");
  }
  return new SetQuery<bool>(Var, Value);
}

QueryRef QueryParser::parseSetOutputKind() {
  StringRef ValStr;
  unsigned OutKind = lexOrCompleteWord<unsigned>(ValStr)
                         .Case("diag", OK_Diag)
                         .Case("print", OK_Print)
                         .Case("dump", OK_Dump)
                         .Default(~0u);
  if (OutKind == ~0u) {
    return new InvalidQuery("expected 'diag', 'print' or 'dump', got '" +
                            ValStr + "'");
  }
  return new SetQuery<OutputKind>(&QuerySession::OutKind, OutputKind(OutKind));
}

QueryRef QueryParser::endQuery(QueryRef Q) {
  const char *Extra = Begin;
  if (!lexWord().empty())
    return new InvalidQuery("unexpected extra input: '" +
                            StringRef(Extra, End - Extra) + "'");
  return Q;
}

namespace {

enum ParsedQueryKind {
  PQK_Invalid,
  PQK_NoOp,
  PQK_Help,
  PQK_Let,
  PQK_Match,
  PQK_Set,
  PQK_Unlet,
};

enum ParsedQueryVariable {
  PQV_Invalid,
  PQV_Output,
  PQV_BindRoot
};

QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) {
  std::string ErrStr;
  llvm::raw_string_ostream OS(ErrStr);
  Diag.printToStreamFull(OS);
  return new InvalidQuery(OS.str());
}

class QuerySessionSema : public Parser::RegistrySema {
public:
  QuerySessionSema(const QuerySession &QS) : QS(QS) {}

  ast_matchers::dynamic::VariantValue getNamedValue(StringRef Name) override {
    return QS.NamedValues.lookup(Name);
  }

private:
  const QuerySession &QS;
};

}  // namespace

QueryRef QueryParser::completeMatcherExpression() {
  std::vector<MatcherCompletion> Comps = Parser::completeExpression(
      StringRef(Begin, End - Begin), CompletionPos - Begin);
  for (std::vector<MatcherCompletion>::iterator I = Comps.begin(),
                                                E = Comps.end();
       I != E; ++I) {
    Completions.push_back(LineEditor::Completion(I->TypedText, I->MatcherDecl));
  }
  return QueryRef();
}

QueryRef QueryParser::doParse() {
  StringRef CommandStr;
  ParsedQueryKind QKind = lexOrCompleteWord<ParsedQueryKind>(CommandStr)
                              .Case("", PQK_NoOp)
                              .Case("help", PQK_Help)
                              .Case("m", PQK_Match, /*IsCompletion=*/false)
                              .Case("let", PQK_Let)
                              .Case("match", PQK_Match)
                              .Case("set", PQK_Set)
                              .Case("unlet", PQK_Unlet)
                              .Default(PQK_Invalid);

  QuerySessionSema S(QS);

  switch (QKind) {
  case PQK_NoOp:
    return new NoOpQuery;

  case PQK_Help:
    return endQuery(new HelpQuery);

  case PQK_Let: {
    StringRef Name = lexWord();

    if (Name.empty())
      return new InvalidQuery("expected variable name");

    if (CompletionPos)
      return completeMatcherExpression();

    Diagnostics Diag;
    ast_matchers::dynamic::VariantValue Value;
    if (!Parser::parseExpression(StringRef(Begin, End - Begin), &S, &Value,
                                 &Diag)) {
      return makeInvalidQueryFromDiagnostics(Diag);
    }

    return new LetQuery(Name, Value);
  }

  case PQK_Match: {
    if (CompletionPos)
      return completeMatcherExpression();

    Diagnostics Diag;
    Optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression(
        StringRef(Begin, End - Begin), &S, &Diag);
    if (!Matcher) {
      return makeInvalidQueryFromDiagnostics(Diag);
    }
    return new MatchQuery(*Matcher);
  }

  case PQK_Set: {
    StringRef VarStr;
    ParsedQueryVariable Var = lexOrCompleteWord<ParsedQueryVariable>(VarStr)
                                  .Case("output", PQV_Output)
                                  .Case("bind-root", PQV_BindRoot)
                                  .Default(PQV_Invalid);
    if (VarStr.empty())
      return new InvalidQuery("expected variable name");
    if (Var == PQV_Invalid)
      return new InvalidQuery("unknown variable: '" + VarStr + "'");

    QueryRef Q;
    switch (Var) {
    case PQV_Output:
      Q = parseSetOutputKind();
      break;
    case PQV_BindRoot:
      Q = parseSetBool(&QuerySession::BindRoot);
      break;
    case PQV_Invalid:
      llvm_unreachable("Invalid query kind");
    }

    return endQuery(Q);
  }

  case PQK_Unlet: {
    StringRef Name = lexWord();

    if (Name.empty())
      return new InvalidQuery("expected variable name");

    return endQuery(new LetQuery(Name, VariantValue()));
  }

  case PQK_Invalid:
    return new InvalidQuery("unknown command: " + CommandStr);
  }

  llvm_unreachable("Invalid query kind");
}

QueryRef QueryParser::parse(StringRef Line, const QuerySession &QS) {
  return QueryParser(Line, QS).doParse();
}

std::vector<LineEditor::Completion>
QueryParser::complete(StringRef Line, size_t Pos, const QuerySession &QS) {
  QueryParser P(Line, QS);
  P.CompletionPos = Line.data() + Pos;

  P.doParse();
  return P.Completions;
}

} // namespace query
} // namespace clang
<commit_msg>[clang-query] Use the new API for named values from the Parser.<commit_after>//===---- QueryParser.cpp - clang-query command parser --------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "QueryParser.h"
#include "Query.h"
#include "QuerySession.h"
#include "clang/ASTMatchers/Dynamic/Parser.h"
#include "clang/Basic/CharInfo.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include <set>

using namespace llvm;
using namespace clang::ast_matchers::dynamic;

namespace clang {
namespace query {

// Lex any amount of whitespace followed by a "word" (any sequence of
// non-whitespace characters) from the start of region [Begin,End).  If no word
// is found before End, return StringRef().  Begin is adjusted to exclude the
// lexed region.
StringRef QueryParser::lexWord() {
  while (true) {
    if (Begin == End)
      return StringRef(Begin, 0);

    if (!isWhitespace(*Begin))
      break;

    ++Begin;
  }

  const char *WordBegin = Begin;

  while (true) {
    ++Begin;

    if (Begin == End || isWhitespace(*Begin))
      return StringRef(WordBegin, Begin - WordBegin);
  }
}

// This is the StringSwitch-alike used by lexOrCompleteWord below. See that
// function for details.
template <typename T> struct QueryParser::LexOrCompleteWord {
  StringSwitch<T> Switch;

  QueryParser *P;
  StringRef Word;
  // Set to the completion point offset in Word, or StringRef::npos if
  // completion point not in Word.
  size_t WordCompletionPos;

  LexOrCompleteWord(QueryParser *P, StringRef Word, size_t WCP)
      : Switch(Word), P(P), Word(Word), WordCompletionPos(WCP) {}

  template <unsigned N>
  LexOrCompleteWord &Case(const char (&S)[N], const T &Value,
                          bool IsCompletion = true) {
    StringRef CaseStr(S, N - 1);

    if (WordCompletionPos == StringRef::npos)
      Switch.Case(S, Value);
    else if (N != 1 && IsCompletion && WordCompletionPos <= CaseStr.size() &&
             CaseStr.substr(0, WordCompletionPos) ==
                 Word.substr(0, WordCompletionPos))
      P->Completions.push_back(LineEditor::Completion(
          (CaseStr.substr(WordCompletionPos) + " ").str(), CaseStr));
    return *this;
  }

  T Default(const T& Value) const {
    return Switch.Default(Value);
  }
};

// Lexes a word and stores it in Word. Returns a LexOrCompleteWord<T> object
// that can be used like a llvm::StringSwitch<T>, but adds cases as possible
// completions if the lexed word contains the completion point.
template <typename T>
QueryParser::LexOrCompleteWord<T>
QueryParser::lexOrCompleteWord(StringRef &Word) {
  Word = lexWord();
  size_t WordCompletionPos = StringRef::npos;
  if (CompletionPos && CompletionPos <= Word.data() + Word.size()) {
    if (CompletionPos < Word.data())
      WordCompletionPos = 0;
    else
      WordCompletionPos = CompletionPos - Word.data();
  }
  return LexOrCompleteWord<T>(this, Word, WordCompletionPos);
}

QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) {
  StringRef ValStr;
  unsigned Value = lexOrCompleteWord<unsigned>(ValStr)
                      .Case("false", 0)
                      .Case("true", 1)
                      .Default(~0u);
  if (Value == ~0u) {
    return new InvalidQuery("expected 'true' or 'false', got '" + ValStr + "'");
  }
  return new SetQuery<bool>(Var, Value);
}

QueryRef QueryParser::parseSetOutputKind() {
  StringRef ValStr;
  unsigned OutKind = lexOrCompleteWord<unsigned>(ValStr)
                         .Case("diag", OK_Diag)
                         .Case("print", OK_Print)
                         .Case("dump", OK_Dump)
                         .Default(~0u);
  if (OutKind == ~0u) {
    return new InvalidQuery("expected 'diag', 'print' or 'dump', got '" +
                            ValStr + "'");
  }
  return new SetQuery<OutputKind>(&QuerySession::OutKind, OutputKind(OutKind));
}

QueryRef QueryParser::endQuery(QueryRef Q) {
  const char *Extra = Begin;
  if (!lexWord().empty())
    return new InvalidQuery("unexpected extra input: '" +
                            StringRef(Extra, End - Extra) + "'");
  return Q;
}

namespace {

enum ParsedQueryKind {
  PQK_Invalid,
  PQK_NoOp,
  PQK_Help,
  PQK_Let,
  PQK_Match,
  PQK_Set,
  PQK_Unlet,
};

enum ParsedQueryVariable {
  PQV_Invalid,
  PQV_Output,
  PQV_BindRoot
};

QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) {
  std::string ErrStr;
  llvm::raw_string_ostream OS(ErrStr);
  Diag.printToStreamFull(OS);
  return new InvalidQuery(OS.str());
}

}  // namespace

QueryRef QueryParser::completeMatcherExpression() {
  std::vector<MatcherCompletion> Comps = Parser::completeExpression(
      StringRef(Begin, End - Begin), CompletionPos - Begin, nullptr,
      &QS.NamedValues);
  for (std::vector<MatcherCompletion>::iterator I = Comps.begin(),
                                                E = Comps.end();
       I != E; ++I) {
    Completions.push_back(LineEditor::Completion(I->TypedText, I->MatcherDecl));
  }
  return QueryRef();
}

QueryRef QueryParser::doParse() {
  StringRef CommandStr;
  ParsedQueryKind QKind = lexOrCompleteWord<ParsedQueryKind>(CommandStr)
                              .Case("", PQK_NoOp)
                              .Case("help", PQK_Help)
                              .Case("m", PQK_Match, /*IsCompletion=*/false)
                              .Case("let", PQK_Let)
                              .Case("match", PQK_Match)
                              .Case("set", PQK_Set)
                              .Case("unlet", PQK_Unlet)
                              .Default(PQK_Invalid);

  switch (QKind) {
  case PQK_NoOp:
    return new NoOpQuery;

  case PQK_Help:
    return endQuery(new HelpQuery);

  case PQK_Let: {
    StringRef Name = lexWord();

    if (Name.empty())
      return new InvalidQuery("expected variable name");

    if (CompletionPos)
      return completeMatcherExpression();

    Diagnostics Diag;
    ast_matchers::dynamic::VariantValue Value;
    if (!Parser::parseExpression(StringRef(Begin, End - Begin), nullptr,
                                 &QS.NamedValues, &Value, &Diag)) {
      return makeInvalidQueryFromDiagnostics(Diag);
    }

    return new LetQuery(Name, Value);
  }

  case PQK_Match: {
    if (CompletionPos)
      return completeMatcherExpression();

    Diagnostics Diag;
    Optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression(
        StringRef(Begin, End - Begin), nullptr, &QS.NamedValues, &Diag);
    if (!Matcher) {
      return makeInvalidQueryFromDiagnostics(Diag);
    }
    return new MatchQuery(*Matcher);
  }

  case PQK_Set: {
    StringRef VarStr;
    ParsedQueryVariable Var = lexOrCompleteWord<ParsedQueryVariable>(VarStr)
                                  .Case("output", PQV_Output)
                                  .Case("bind-root", PQV_BindRoot)
                                  .Default(PQV_Invalid);
    if (VarStr.empty())
      return new InvalidQuery("expected variable name");
    if (Var == PQV_Invalid)
      return new InvalidQuery("unknown variable: '" + VarStr + "'");

    QueryRef Q;
    switch (Var) {
    case PQV_Output:
      Q = parseSetOutputKind();
      break;
    case PQV_BindRoot:
      Q = parseSetBool(&QuerySession::BindRoot);
      break;
    case PQV_Invalid:
      llvm_unreachable("Invalid query kind");
    }

    return endQuery(Q);
  }

  case PQK_Unlet: {
    StringRef Name = lexWord();

    if (Name.empty())
      return new InvalidQuery("expected variable name");

    return endQuery(new LetQuery(Name, VariantValue()));
  }

  case PQK_Invalid:
    return new InvalidQuery("unknown command: " + CommandStr);
  }

  llvm_unreachable("Invalid query kind");
}

QueryRef QueryParser::parse(StringRef Line, const QuerySession &QS) {
  return QueryParser(Line, QS).doParse();
}

std::vector<LineEditor::Completion>
QueryParser::complete(StringRef Line, size_t Pos, const QuerySession &QS) {
  QueryParser P(Line, QS);
  P.CompletionPos = Line.data() + Pos;

  P.doParse();
  return P.Completions;
}

} // namespace query
} // namespace clang
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
** Author: Andreas Hartmetz, KDAB (andreas.hartmetz@kdab.com)
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "analyzerruncontrol.h"
#include "ianalyzertool.h"
#include "analyzermanager.h"
#include "analyzerstartparameters.h"

#include <QDebug>
#include <QAction>

using namespace ProjectExplorer;

//////////////////////////////////////////////////////////////////////////
//
// AnalyzerRunControl
//
//////////////////////////////////////////////////////////////////////////

namespace Analyzer {

AnalyzerRunControl::AnalyzerRunControl(const AnalyzerStartParameters &sp,
        ProjectExplorer::RunConfiguration *runConfiguration)
    : RunControl(runConfiguration, sp.runMode)
{
    m_runConfig = runConfiguration;
    m_sp = sp;

    connect(this, SIGNAL(finished()), SLOT(runControlFinished()));
    connect(AnalyzerManager::stopAction(), SIGNAL(triggered()), SLOT(stopIt()));
}

void AnalyzerRunControl::stopIt()
{
    if (stop() == RunControl::StoppedSynchronously)
        AnalyzerManager::handleToolFinished();
}

void AnalyzerRunControl::runControlFinished()
{
    m_isRunning = false;
    AnalyzerManager::handleToolFinished();
    emit finished();
}

void AnalyzerRunControl::start()
{
    AnalyzerManager::handleToolStarted();

    if (startEngine()) {
        m_isRunning = true;
        emit started();
    }
}

RunControl::StopResult AnalyzerRunControl::stop()
{
    if (!m_isRunning)
        return StoppedSynchronously;

    stopEngine();
    m_isRunning = false;
    return AsynchronousStop;
}

bool AnalyzerRunControl::isRunning() const
{
    return m_isRunning;
}

QString AnalyzerRunControl::displayName() const
{
    return m_runConfig ? m_runConfig->displayName() : m_sp.displayName;
}

QIcon AnalyzerRunControl::icon() const
{
    return QIcon(QLatin1String(":/images/analyzer_start_small.png"));
}

} // namespace Analyzer
<commit_msg>Analyzer: Fix endless loop after finishing<commit_after>/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
** Author: Andreas Hartmetz, KDAB (andreas.hartmetz@kdab.com)
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "analyzerruncontrol.h"
#include "ianalyzertool.h"
#include "analyzermanager.h"
#include "analyzerstartparameters.h"

#include <QDebug>
#include <QAction>

using namespace ProjectExplorer;

//////////////////////////////////////////////////////////////////////////
//
// AnalyzerRunControl
//
//////////////////////////////////////////////////////////////////////////

namespace Analyzer {

AnalyzerRunControl::AnalyzerRunControl(const AnalyzerStartParameters &sp,
        ProjectExplorer::RunConfiguration *runConfiguration)
    : RunControl(runConfiguration, sp.runMode)
{
    m_runConfig = runConfiguration;
    m_sp = sp;

    connect(this, SIGNAL(finished()), SLOT(runControlFinished()));
    connect(AnalyzerManager::stopAction(), SIGNAL(triggered()), SLOT(stopIt()));
}

void AnalyzerRunControl::stopIt()
{
    if (stop() == RunControl::StoppedSynchronously)
        AnalyzerManager::handleToolFinished();
}

void AnalyzerRunControl::runControlFinished()
{
    m_isRunning = false;
    AnalyzerManager::handleToolFinished();
}

void AnalyzerRunControl::start()
{
    AnalyzerManager::handleToolStarted();

    if (startEngine()) {
        m_isRunning = true;
        emit started();
    }
}

RunControl::StopResult AnalyzerRunControl::stop()
{
    if (!m_isRunning)
        return StoppedSynchronously;

    stopEngine();
    m_isRunning = false;
    return AsynchronousStop;
}

bool AnalyzerRunControl::isRunning() const
{
    return m_isRunning;
}

QString AnalyzerRunControl::displayName() const
{
    return m_runConfig ? m_runConfig->displayName() : m_sp.displayName;
}

QIcon AnalyzerRunControl::icon() const
{
    return QIcon(QLatin1String(":/images/analyzer_start_small.png"));
}

} // namespace Analyzer
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "qmljsclientproxy.h"
#include "qmljsdebuggerclient.h"
#include "qmljsprivateapi.h"
#include "qmljsdesigndebugclient.h"

#include <utils/qtcassert.h>

#include <QUrl>
#include <QAbstractSocket>
#include <QDebug>

using namespace QmlJSInspector::Internal;

ClientProxy *ClientProxy::m_instance = 0;

ClientProxy::ClientProxy(QObject *parent) :
    QObject(parent),
    m_conn(0),
    m_client(0),
    m_designClient(0),
    m_engineQuery(0),
    m_contextQuery(0),
    m_objectTreeQuery(0)
{
    Q_ASSERT(! m_instance);
    m_instance = this;
}

ClientProxy *ClientProxy::instance()
{
    return m_instance;
}

bool ClientProxy::connectToViewer(const QString &host, quint16 port)
{
    if (m_conn && m_conn->state() != QAbstractSocket::UnconnectedState)
        return false;

    if (m_designClient) {

        disconnect(m_designClient, SIGNAL(currentObjectsChanged(QList<int>)),
                            this, SLOT(onCurrentObjectsChanged(QList<int>)));
        disconnect(m_designClient,
                   SIGNAL(colorPickerActivated()), this, SIGNAL(colorPickerActivated()));
        disconnect(m_designClient,
                   SIGNAL(zoomToolActivated()), this, SIGNAL(zoomToolActivated()));
        disconnect(m_designClient,
                   SIGNAL(selectToolActivated()), this, SIGNAL(selectToolActivated()));
        disconnect(m_designClient,
                   SIGNAL(selectMarqueeToolActivated()), this, SIGNAL(selectMarqueeToolActivated()));
        disconnect(m_designClient,
                   SIGNAL(animationSpeedChanged(qreal)), this, SIGNAL(animationSpeedChanged(qreal)));
        disconnect(m_designClient,
                   SIGNAL(designModeBehaviorChanged(bool)), this, SIGNAL(designModeBehaviorChanged(bool)));

        emit aboutToDisconnect();

        delete m_client;
        m_client = 0;

        delete m_designClient;
        m_designClient = 0;
    }

    if (m_conn) {
        m_conn->disconnectFromHost();
        delete m_conn;
        m_conn = 0;
    }

    m_conn = new QDeclarativeDebugConnection(this);
    connect(m_conn, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
            SLOT(connectionStateChanged()));
    connect(m_conn, SIGNAL(error(QAbstractSocket::SocketError)),
            SLOT(connectionError()));

    emit connectionStatusMessage(tr("[Inspector] set to connect to debug server %1:%2").arg(host).arg(port));
    m_conn->connectToHost(host, port);

    // blocks until connected; if no connection is available, will fail immediately
    if (!m_conn->waitForConnected())
        return false;

    return true;
}

void ClientProxy::refreshObjectTree()
{
    if (!m_objectTreeQuery) {
        m_objectTreeQuery = m_client->queryObjectRecursive(m_rootObject, this);

        if (!m_objectTreeQuery->isWaiting()) {
            objectTreeFetched(m_objectTreeQuery->state());
        } else {
            connect(m_objectTreeQuery,
                    SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
                    SLOT(objectTreeFetched(QDeclarativeDebugQuery::State)));
        }
    }
}

void ClientProxy::onCurrentObjectsChanged(const QList<int> &debugIds)
{
    QList<QDeclarativeDebugObjectReference> selectedItems;

    foreach(int debugId, debugIds) {
        QDeclarativeDebugObjectReference ref = objectReferenceForId(debugId);
        if (ref.debugId() != -1) {
            selectedItems << ref;
        } else {
            // ### FIXME right now, there's no way in the protocol to
            // a) get some item and know its parent (although that's possible by adding it to a separate plugin)
            // b) add children to part of an existing tree.
            // So the only choice that remains is to update the complete tree when we have an unknown debug id.
            if (!m_objectTreeQuery)
                m_objectTreeQuery = m_client->queryObjectRecursive(m_rootObject, this);
            break;
        }
    }

    if (m_objectTreeQuery) {
        if (!m_objectTreeQuery->isWaiting()) {
            objectTreeFetched(m_objectTreeQuery->state());
        } else {
            connect(m_objectTreeQuery,
                    SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
                    SLOT(objectTreeFetched(QDeclarativeDebugQuery::State)));
        }
    } else {
        emit selectedItemsChanged(selectedItems);
    }
}

void ClientProxy::disconnectFromViewer()
{
    m_conn->disconnectFromHost();
    emit disconnected();
}

void ClientProxy::connectionError()
{
    emit connectionStatusMessage(tr("[Inspector] error: (%1) %2", "%1=error code, %2=error message")
                                .arg(m_conn->error()).arg(m_conn->errorString()));
}

void ClientProxy::connectionStateChanged()
{
    switch (m_conn->state()) {
        case QAbstractSocket::UnconnectedState:
        {
            emit connectionStatusMessage(tr("[Inspector] disconnected.\n\n"));

            delete m_engineQuery;
            m_engineQuery = 0;
            delete m_contextQuery;
            m_contextQuery = 0;

            if (m_objectTreeQuery) {
                delete m_objectTreeQuery;
                m_objectTreeQuery = 0;
            }

            emit disconnected();

            break;
        }
        case QAbstractSocket::HostLookupState:
            emit connectionStatusMessage(tr("[Inspector] resolving host..."));
            break;
        case QAbstractSocket::ConnectingState:
            emit connectionStatusMessage(tr("[Inspector] connecting to debug server..."));
            break;
        case QAbstractSocket::ConnectedState:
        {
            emit connectionStatusMessage(tr("[Inspector] connected.\n"));

            if (!m_client) {
                m_client = new QDeclarativeEngineDebug(m_conn, this);
                m_designClient = new QmlJSDesignDebugClient(m_conn, this);
                emit connected(m_client);

                connect(m_designClient,
                        SIGNAL(currentObjectsChanged(QList<int>)),
                        SLOT(onCurrentObjectsChanged(QList<int>)));
                connect(m_designClient,
                        SIGNAL(colorPickerActivated()), SIGNAL(colorPickerActivated()));
                connect(m_designClient,
                        SIGNAL(zoomToolActivated()), SIGNAL(zoomToolActivated()));
                connect(m_designClient,
                        SIGNAL(selectToolActivated()), SIGNAL(selectToolActivated()));
                connect(m_designClient,
                        SIGNAL(selectMarqueeToolActivated()), SIGNAL(selectMarqueeToolActivated()));
                connect(m_designClient,
                        SIGNAL(animationSpeedChanged(qreal)), SIGNAL(animationSpeedChanged(qreal)));
                connect(m_designClient,
                        SIGNAL(designModeBehaviorChanged(bool)), SIGNAL(designModeBehaviorChanged(bool)));
                connect(m_designClient, SIGNAL(reloaded()), this, SIGNAL(serverReloaded()));
            }

            (void) new DebuggerClient(m_conn);

            reloadEngines();

            break;
        }
        case QAbstractSocket::ClosingState:
            emit connectionStatusMessage(tr("[Inspector] closing..."));
            break;
        case QAbstractSocket::BoundState:
        case QAbstractSocket::ListeningState:
            break;
    }
}

bool ClientProxy::isConnected() const
{
    return (m_conn && m_client && m_conn->state() == QAbstractSocket::ConnectedState);
}

bool ClientProxy::isUnconnected() const
{
    return (!m_conn || m_conn->state() == QAbstractSocket::UnconnectedState);
}

void ClientProxy::setSelectedItemsByObjectId(const QList<QDeclarativeDebugObjectReference> &objectRefs)
{
    if (isConnected() && m_designClient)
        m_designClient->setSelectedItemsByObjectId(objectRefs);
}

QDeclarativeDebugObjectReference ClientProxy::objectReferenceForId(int debugId) const
{
    return objectReferenceForId(debugId, m_rootObject);
}

QDeclarativeDebugObjectReference QmlJSInspector::Internal::ClientProxy::rootObjectReference() const
{
    return m_rootObject;
}

QDeclarativeDebugObjectReference ClientProxy::objectReferenceForId(int debugId,
                                                                   const QDeclarativeDebugObjectReference &objectRef) const
{
    if (objectRef.debugId() == debugId)
        return objectRef;

    foreach(const QDeclarativeDebugObjectReference &child, objectRef.children()) {
        QDeclarativeDebugObjectReference result = objectReferenceForId(debugId, child);
        if (result.debugId() == debugId)
            return result;
    }

    return QDeclarativeDebugObjectReference();
}

QList<QDeclarativeDebugObjectReference> ClientProxy::objectReferences(const QUrl &url) const
{
    return objectReferences(url, m_rootObject);
}

QList<QDeclarativeDebugObjectReference> ClientProxy::objectReferences(const QUrl &url,
                                                                      const QDeclarativeDebugObjectReference &objectRef) const
{
    QList<QDeclarativeDebugObjectReference> result;
    if (objectRef.source().url() == url || url.isEmpty())
        result.append(objectRef);

    foreach(const QDeclarativeDebugObjectReference &child, objectRef.children()) {
        result.append(objectReferences(url, child));
    }

    return result;
}

bool ClientProxy::setBindingForObject(int objectDebugId,
                                      const QString &propertyName,
                                      const QVariant &value,
                                      bool isLiteralValue)
{
    qDebug() << "setBindingForObject():" << objectDebugId << propertyName << value;
    if (objectDebugId == -1)
        return false;

    if (propertyName == QLatin1String("id"))
        return false; // Crashes the QMLViewer.

    return m_client->setBindingForObject(objectDebugId, propertyName, value.toString(), isLiteralValue);
}

bool ClientProxy::setMethodBodyForObject(int objectDebugId, const QString &methodName, const QString &methodBody)
{
    qDebug() << "setMethodBodyForObject():" << objectDebugId << methodName << methodBody;
    if (objectDebugId == -1)
        return 0;
    return m_client->setMethodBody(objectDebugId, methodName, methodBody);
}

bool ClientProxy::resetBindingForObject(int objectDebugId, const QString& propertyName)
{
    qDebug() << "resetBindingForObject():" << objectDebugId << propertyName;
    if (objectDebugId == -1)
        return false;
    //    if (propertyName == QLatin1String("id"))  return false;
    return m_client->resetBindingForObject(objectDebugId, propertyName);
}

void ClientProxy::queryEngineContext(int id)
{
    if (id < 0)
        return;

    if (m_contextQuery) {
        delete m_contextQuery;
        m_contextQuery = 0;
    }

    m_contextQuery = m_client->queryRootContexts(QDeclarativeDebugEngineReference(id), this);
    if (!m_contextQuery->isWaiting())
        contextChanged();
    else
        connect(m_contextQuery, SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
                this, SLOT(contextChanged()));
}

void ClientProxy::contextChanged()
{
    if (m_contextQuery) {

        if (m_contextQuery->rootContext().objects().isEmpty()) {
            m_rootObject = QDeclarativeDebugObjectReference();
            emit objectTreeUpdated(m_rootObject);
        } else {
            m_rootObject = m_contextQuery->rootContext().objects().first();
        }

        delete m_contextQuery;
        m_contextQuery = 0;

        m_objectTreeQuery = m_client->queryObjectRecursive(m_rootObject, this);
        if (!m_objectTreeQuery->isWaiting()) {
            objectTreeFetched();
        } else {
            connect(m_objectTreeQuery,
                    SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
                    SLOT(objectTreeFetched(QDeclarativeDebugQuery::State)));
        }
    }

}

void ClientProxy::objectTreeFetched(QDeclarativeDebugQuery::State state)
{
    if (state == QDeclarativeDebugQuery::Error) {
        delete m_objectTreeQuery;
        m_objectTreeQuery = 0;
    }

    if (state != QDeclarativeDebugQuery::Completed) {
        m_rootObject = QDeclarativeDebugObjectReference();
        return;
    }

    m_rootObject = m_objectTreeQuery->object();

    delete m_objectTreeQuery;
    m_objectTreeQuery = 0;

    emit objectTreeUpdated(m_rootObject);

    if (isDesignClientConnected() && !m_designClient->selectedItemIds().isEmpty()) {
        onCurrentObjectsChanged(m_designClient->selectedItemIds());
    }

}

void ClientProxy::reloadQmlViewer()
{
    if (isDesignClientConnected())
        m_designClient->reloadViewer();
}

void ClientProxy::setDesignModeBehavior(bool inDesignMode)
{
    if (isDesignClientConnected())
        m_designClient->setDesignModeBehavior(inDesignMode);
}

void ClientProxy::setAnimationSpeed(qreal slowdownFactor)
{
    if (isDesignClientConnected())
        m_designClient->setAnimationSpeed(slowdownFactor);
}

void ClientProxy::changeToColorPickerTool()
{
    if (isDesignClientConnected())
        m_designClient->changeToColorPickerTool();
}

void ClientProxy::changeToZoomTool()
{
    if (isDesignClientConnected())
        m_designClient->changeToZoomTool();
}
void ClientProxy::changeToSelectTool()
{
    if (isDesignClientConnected())
        m_designClient->changeToSelectTool();
}

void ClientProxy::changeToSelectMarqueeTool()
{
    if (isDesignClientConnected())
        m_designClient->changeToSelectMarqueeTool();
}

void ClientProxy::createQmlObject(const QString &qmlText, int parentDebugId,
                                  const QStringList &imports, const QString &filename)
{
    if (isDesignClientConnected())
        m_designClient->createQmlObject(qmlText, parentDebugId, imports, filename);
}

void QmlJSInspector::Internal::ClientProxy::destroyQmlObject(int debugId)
{
    if (isDesignClientConnected())
        m_designClient->destroyQmlObject(debugId);
}

bool ClientProxy::isDesignClientConnected() const
{
    return (m_designClient && m_conn->isConnected());
}

void ClientProxy::reloadEngines()
{
    if (m_engineQuery) {
        emit connectionStatusMessage("[Inspector] Waiting for response to previous engine query");
        return;
    }

    emit aboutToReloadEngines();

    m_engineQuery = m_client->queryAvailableEngines(this);
    if (!m_engineQuery->isWaiting())
        updateEngineList();
    else
        connect(m_engineQuery, SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
                         this, SLOT(updateEngineList()));
}

QList<QDeclarativeDebugEngineReference> ClientProxy::engines() const
{
    return m_engines;
}

void ClientProxy::updateEngineList()
{
    m_engines = m_engineQuery->engines();
    delete m_engineQuery;
    m_engineQuery = 0;

    emit enginesChanged();
}
<commit_msg>QML Live Preview: Apparently, the interresting root object is the last.<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "qmljsclientproxy.h"
#include "qmljsdebuggerclient.h"
#include "qmljsprivateapi.h"
#include "qmljsdesigndebugclient.h"

#include <utils/qtcassert.h>

#include <QUrl>
#include <QAbstractSocket>
#include <QDebug>

using namespace QmlJSInspector::Internal;

ClientProxy *ClientProxy::m_instance = 0;

ClientProxy::ClientProxy(QObject *parent) :
    QObject(parent),
    m_conn(0),
    m_client(0),
    m_designClient(0),
    m_engineQuery(0),
    m_contextQuery(0),
    m_objectTreeQuery(0)
{
    Q_ASSERT(! m_instance);
    m_instance = this;
}

ClientProxy *ClientProxy::instance()
{
    return m_instance;
}

bool ClientProxy::connectToViewer(const QString &host, quint16 port)
{
    if (m_conn && m_conn->state() != QAbstractSocket::UnconnectedState)
        return false;

    if (m_designClient) {

        disconnect(m_designClient, SIGNAL(currentObjectsChanged(QList<int>)),
                            this, SLOT(onCurrentObjectsChanged(QList<int>)));
        disconnect(m_designClient,
                   SIGNAL(colorPickerActivated()), this, SIGNAL(colorPickerActivated()));
        disconnect(m_designClient,
                   SIGNAL(zoomToolActivated()), this, SIGNAL(zoomToolActivated()));
        disconnect(m_designClient,
                   SIGNAL(selectToolActivated()), this, SIGNAL(selectToolActivated()));
        disconnect(m_designClient,
                   SIGNAL(selectMarqueeToolActivated()), this, SIGNAL(selectMarqueeToolActivated()));
        disconnect(m_designClient,
                   SIGNAL(animationSpeedChanged(qreal)), this, SIGNAL(animationSpeedChanged(qreal)));
        disconnect(m_designClient,
                   SIGNAL(designModeBehaviorChanged(bool)), this, SIGNAL(designModeBehaviorChanged(bool)));

        emit aboutToDisconnect();

        delete m_client;
        m_client = 0;

        delete m_designClient;
        m_designClient = 0;
    }

    if (m_conn) {
        m_conn->disconnectFromHost();
        delete m_conn;
        m_conn = 0;
    }

    m_conn = new QDeclarativeDebugConnection(this);
    connect(m_conn, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
            SLOT(connectionStateChanged()));
    connect(m_conn, SIGNAL(error(QAbstractSocket::SocketError)),
            SLOT(connectionError()));

    emit connectionStatusMessage(tr("[Inspector] set to connect to debug server %1:%2").arg(host).arg(port));
    m_conn->connectToHost(host, port);

    // blocks until connected; if no connection is available, will fail immediately
    if (!m_conn->waitForConnected())
        return false;

    return true;
}

void ClientProxy::refreshObjectTree()
{
    if (!m_objectTreeQuery) {
        m_objectTreeQuery = m_client->queryObjectRecursive(m_rootObject, this);

        if (!m_objectTreeQuery->isWaiting()) {
            objectTreeFetched(m_objectTreeQuery->state());
        } else {
            connect(m_objectTreeQuery,
                    SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
                    SLOT(objectTreeFetched(QDeclarativeDebugQuery::State)));
        }
    }
}

void ClientProxy::onCurrentObjectsChanged(const QList<int> &debugIds)
{
    QList<QDeclarativeDebugObjectReference> selectedItems;

    foreach(int debugId, debugIds) {
        QDeclarativeDebugObjectReference ref = objectReferenceForId(debugId);
        if (ref.debugId() != -1) {
            selectedItems << ref;
        } else {
            // ### FIXME right now, there's no way in the protocol to
            // a) get some item and know its parent (although that's possible by adding it to a separate plugin)
            // b) add children to part of an existing tree.
            // So the only choice that remains is to update the complete tree when we have an unknown debug id.
            if (!m_objectTreeQuery)
                m_objectTreeQuery = m_client->queryObjectRecursive(m_rootObject, this);
            break;
        }
    }

    if (m_objectTreeQuery) {
        if (!m_objectTreeQuery->isWaiting()) {
            objectTreeFetched(m_objectTreeQuery->state());
        } else {
            connect(m_objectTreeQuery,
                    SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
                    SLOT(objectTreeFetched(QDeclarativeDebugQuery::State)));
        }
    } else {
        emit selectedItemsChanged(selectedItems);
    }
}

void ClientProxy::disconnectFromViewer()
{
    m_conn->disconnectFromHost();
    emit disconnected();
}

void ClientProxy::connectionError()
{
    emit connectionStatusMessage(tr("[Inspector] error: (%1) %2", "%1=error code, %2=error message")
                                .arg(m_conn->error()).arg(m_conn->errorString()));
}

void ClientProxy::connectionStateChanged()
{
    switch (m_conn->state()) {
        case QAbstractSocket::UnconnectedState:
        {
            emit connectionStatusMessage(tr("[Inspector] disconnected.\n\n"));

            delete m_engineQuery;
            m_engineQuery = 0;
            delete m_contextQuery;
            m_contextQuery = 0;

            if (m_objectTreeQuery) {
                delete m_objectTreeQuery;
                m_objectTreeQuery = 0;
            }

            emit disconnected();

            break;
        }
        case QAbstractSocket::HostLookupState:
            emit connectionStatusMessage(tr("[Inspector] resolving host..."));
            break;
        case QAbstractSocket::ConnectingState:
            emit connectionStatusMessage(tr("[Inspector] connecting to debug server..."));
            break;
        case QAbstractSocket::ConnectedState:
        {
            emit connectionStatusMessage(tr("[Inspector] connected.\n"));

            if (!m_client) {
                m_client = new QDeclarativeEngineDebug(m_conn, this);
                m_designClient = new QmlJSDesignDebugClient(m_conn, this);
                emit connected(m_client);

                connect(m_designClient,
                        SIGNAL(currentObjectsChanged(QList<int>)),
                        SLOT(onCurrentObjectsChanged(QList<int>)));
                connect(m_designClient,
                        SIGNAL(colorPickerActivated()), SIGNAL(colorPickerActivated()));
                connect(m_designClient,
                        SIGNAL(zoomToolActivated()), SIGNAL(zoomToolActivated()));
                connect(m_designClient,
                        SIGNAL(selectToolActivated()), SIGNAL(selectToolActivated()));
                connect(m_designClient,
                        SIGNAL(selectMarqueeToolActivated()), SIGNAL(selectMarqueeToolActivated()));
                connect(m_designClient,
                        SIGNAL(animationSpeedChanged(qreal)), SIGNAL(animationSpeedChanged(qreal)));
                connect(m_designClient,
                        SIGNAL(designModeBehaviorChanged(bool)), SIGNAL(designModeBehaviorChanged(bool)));
                connect(m_designClient, SIGNAL(reloaded()), this, SIGNAL(serverReloaded()));
            }

            (void) new DebuggerClient(m_conn);

            reloadEngines();

            break;
        }
        case QAbstractSocket::ClosingState:
            emit connectionStatusMessage(tr("[Inspector] closing..."));
            break;
        case QAbstractSocket::BoundState:
        case QAbstractSocket::ListeningState:
            break;
    }
}

bool ClientProxy::isConnected() const
{
    return (m_conn && m_client && m_conn->state() == QAbstractSocket::ConnectedState);
}

bool ClientProxy::isUnconnected() const
{
    return (!m_conn || m_conn->state() == QAbstractSocket::UnconnectedState);
}

void ClientProxy::setSelectedItemsByObjectId(const QList<QDeclarativeDebugObjectReference> &objectRefs)
{
    if (isConnected() && m_designClient)
        m_designClient->setSelectedItemsByObjectId(objectRefs);
}

QDeclarativeDebugObjectReference ClientProxy::objectReferenceForId(int debugId) const
{
    return objectReferenceForId(debugId, m_rootObject);
}

QDeclarativeDebugObjectReference QmlJSInspector::Internal::ClientProxy::rootObjectReference() const
{
    return m_rootObject;
}

QDeclarativeDebugObjectReference ClientProxy::objectReferenceForId(int debugId,
                                                                   const QDeclarativeDebugObjectReference &objectRef) const
{
    if (objectRef.debugId() == debugId)
        return objectRef;

    foreach(const QDeclarativeDebugObjectReference &child, objectRef.children()) {
        QDeclarativeDebugObjectReference result = objectReferenceForId(debugId, child);
        if (result.debugId() == debugId)
            return result;
    }

    return QDeclarativeDebugObjectReference();
}

QList<QDeclarativeDebugObjectReference> ClientProxy::objectReferences(const QUrl &url) const
{
    return objectReferences(url, m_rootObject);
}

QList<QDeclarativeDebugObjectReference> ClientProxy::objectReferences(const QUrl &url,
                                                                      const QDeclarativeDebugObjectReference &objectRef) const
{
    QList<QDeclarativeDebugObjectReference> result;
    if (objectRef.source().url() == url || url.isEmpty())
        result.append(objectRef);

    foreach(const QDeclarativeDebugObjectReference &child, objectRef.children()) {
        result.append(objectReferences(url, child));
    }

    return result;
}

bool ClientProxy::setBindingForObject(int objectDebugId,
                                      const QString &propertyName,
                                      const QVariant &value,
                                      bool isLiteralValue)
{
    qDebug() << "setBindingForObject():" << objectDebugId << propertyName << value;
    if (objectDebugId == -1)
        return false;

    if (propertyName == QLatin1String("id"))
        return false; // Crashes the QMLViewer.

    return m_client->setBindingForObject(objectDebugId, propertyName, value.toString(), isLiteralValue);
}

bool ClientProxy::setMethodBodyForObject(int objectDebugId, const QString &methodName, const QString &methodBody)
{
    qDebug() << "setMethodBodyForObject():" << objectDebugId << methodName << methodBody;
    if (objectDebugId == -1)
        return 0;
    return m_client->setMethodBody(objectDebugId, methodName, methodBody);
}

bool ClientProxy::resetBindingForObject(int objectDebugId, const QString& propertyName)
{
    qDebug() << "resetBindingForObject():" << objectDebugId << propertyName;
    if (objectDebugId == -1)
        return false;
    //    if (propertyName == QLatin1String("id"))  return false;
    return m_client->resetBindingForObject(objectDebugId, propertyName);
}

void ClientProxy::queryEngineContext(int id)
{
    if (id < 0)
        return;

    if (m_contextQuery) {
        delete m_contextQuery;
        m_contextQuery = 0;
    }

    m_contextQuery = m_client->queryRootContexts(QDeclarativeDebugEngineReference(id), this);
    if (!m_contextQuery->isWaiting())
        contextChanged();
    else
        connect(m_contextQuery, SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
                this, SLOT(contextChanged()));
}

void ClientProxy::contextChanged()
{
    if (m_contextQuery) {

        if (m_contextQuery->rootContext().objects().isEmpty()) {
            m_rootObject = QDeclarativeDebugObjectReference();
            emit objectTreeUpdated(m_rootObject);
        } else {
            m_rootObject = m_contextQuery->rootContext().objects().last();
        }

        delete m_contextQuery;
        m_contextQuery = 0;

        m_objectTreeQuery = m_client->queryObjectRecursive(m_rootObject, this);
        if (!m_objectTreeQuery->isWaiting()) {
            objectTreeFetched();
        } else {
            connect(m_objectTreeQuery,
                    SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
                    SLOT(objectTreeFetched(QDeclarativeDebugQuery::State)));
        }
    }

}

void ClientProxy::objectTreeFetched(QDeclarativeDebugQuery::State state)
{
    if (state == QDeclarativeDebugQuery::Error) {
        delete m_objectTreeQuery;
        m_objectTreeQuery = 0;
    }

    if (state != QDeclarativeDebugQuery::Completed) {
        m_rootObject = QDeclarativeDebugObjectReference();
        return;
    }

    m_rootObject = m_objectTreeQuery->object();

    delete m_objectTreeQuery;
    m_objectTreeQuery = 0;

    emit objectTreeUpdated(m_rootObject);

    if (isDesignClientConnected() && !m_designClient->selectedItemIds().isEmpty()) {
        onCurrentObjectsChanged(m_designClient->selectedItemIds());
    }

}

void ClientProxy::reloadQmlViewer()
{
    if (isDesignClientConnected())
        m_designClient->reloadViewer();
}

void ClientProxy::setDesignModeBehavior(bool inDesignMode)
{
    if (isDesignClientConnected())
        m_designClient->setDesignModeBehavior(inDesignMode);
}

void ClientProxy::setAnimationSpeed(qreal slowdownFactor)
{
    if (isDesignClientConnected())
        m_designClient->setAnimationSpeed(slowdownFactor);
}

void ClientProxy::changeToColorPickerTool()
{
    if (isDesignClientConnected())
        m_designClient->changeToColorPickerTool();
}

void ClientProxy::changeToZoomTool()
{
    if (isDesignClientConnected())
        m_designClient->changeToZoomTool();
}
void ClientProxy::changeToSelectTool()
{
    if (isDesignClientConnected())
        m_designClient->changeToSelectTool();
}

void ClientProxy::changeToSelectMarqueeTool()
{
    if (isDesignClientConnected())
        m_designClient->changeToSelectMarqueeTool();
}

void ClientProxy::createQmlObject(const QString &qmlText, int parentDebugId,
                                  const QStringList &imports, const QString &filename)
{
    if (isDesignClientConnected())
        m_designClient->createQmlObject(qmlText, parentDebugId, imports, filename);
}

void QmlJSInspector::Internal::ClientProxy::destroyQmlObject(int debugId)
{
    if (isDesignClientConnected())
        m_designClient->destroyQmlObject(debugId);
}

bool ClientProxy::isDesignClientConnected() const
{
    return (m_designClient && m_conn->isConnected());
}

void ClientProxy::reloadEngines()
{
    if (m_engineQuery) {
        emit connectionStatusMessage("[Inspector] Waiting for response to previous engine query");
        return;
    }

    emit aboutToReloadEngines();

    m_engineQuery = m_client->queryAvailableEngines(this);
    if (!m_engineQuery->isWaiting())
        updateEngineList();
    else
        connect(m_engineQuery, SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
                         this, SLOT(updateEngineList()));
}

QList<QDeclarativeDebugEngineReference> ClientProxy::engines() const
{
    return m_engines;
}

void ClientProxy::updateEngineList()
{
    m_engines = m_engineQuery->engines();
    delete m_engineQuery;
    m_engineQuery = 0;

    emit enginesChanged();
}
<|endoftext|>