text:
#include "verifier.h"

#include "glog/logging.h"
#include "google/protobuf/text_format.h"

#include "assertions.h"
#include "kythe/cxx/common/kythe_uri.h"
#include "kythe/proto/common.pb.h"
#include "kythe/proto/storage.pb.h"

namespace kythe {
namespace verifier {
namespace {

typedef std::vector<AstNode *> Database;

/// \brief The return code from a verifier thunk.
using ThunkRet = size_t;
/// \brief The operation failed normally.
static ThunkRet kNoException = {0};
/// \brief There is no more work to do, so unwind.
static ThunkRet kSolved = {1};
/// \brief The program is invalid, so unwind.
static ThunkRet kInvalidProgram = {2};
/// \brief The goal group is known to be impossible to solve.
static ThunkRet kImpossible = {3};
/// \brief ThunkRets >= kFirstCut should unwind to the frame
/// establishing that cut without changing assignments.
static ThunkRet kFirstCut = {4};

typedef const std::function<ThunkRet()> &Thunk;

static std::string *kDefaultDatabase = new std::string("builtin");
static std::string *kStandardIn = new std::string("-");

static bool EncodedIdentEqualTo(AstNode *a, AstNode *b) {
  Identifier *ia = a->AsIdentifier();
  Identifier *ib = b->AsIdentifier();
  return ia->symbol() == ib->symbol();
}

static bool EncodedIdentLessThan(AstNode *a, AstNode *b) {
  Identifier *ia = a->AsIdentifier();
  Identifier *ib = b->AsIdentifier();
  return ia->symbol() < ib->symbol();
}

static bool EncodedVNameEqualTo(App *a, App *b) {
  Tuple *ta = a->rhs()->AsTuple();
  Tuple *tb = b->rhs()->AsTuple();
  for (int i = 0; i < 5; ++i) {
    if (!EncodedIdentEqualTo(ta->element(i), tb->element(i))) {
      return false;
    }
  }
  return true;
}

static bool EncodedVNameLessThan(App *a, App *b) {
  Tuple *ta = a->rhs()->AsTuple();
  Tuple *tb = b->rhs()->AsTuple();
  for (int i = 0; i < 4; ++i) {
    if (EncodedIdentLessThan(ta->element(i), tb->element(i))) {
      return true;
    }
    if (!EncodedIdentEqualTo(ta->element(i), tb->element(i))) {
      return false;
    }
  }
  return EncodedIdentLessThan(ta->element(4), tb->element(4));
}

static bool EncodedVNameOrIdentLessThan(AstNode *a, AstNode *b) {
  App *aa = a->AsApp();  // nullptr if a is not a vname
  App *ab = b->AsApp();  // nullptr if b is not a vname
  if (aa && ab) {
    return EncodedVNameLessThan(aa, ab);
  } else if (!aa && ab) {
    // Arbitrarily, vname < ident.
    return true;
  } else if (aa && !ab) {
    return false;
  } else {
    return EncodedIdentLessThan(a, b);
  }
}

static bool EncodedVNameOrIdentEqualTo(AstNode *a, AstNode *b) {
  App *aa = a->AsApp();  // nullptr if a is not a vname
  App *ab = b->AsApp();  // nullptr if b is not a vname
  if (aa && ab) {
    return EncodedVNameEqualTo(aa, ab);
  } else if (!aa && ab) {
    return false;
  } else if (aa && !ab) {
    return false;
  } else {
    return EncodedIdentEqualTo(a, b);
  }
}

/// \brief Sort entries such that those that set fact values are adjacent.
static bool EncodedFactLessThan(AstNode *a, AstNode *b) {
  Tuple *ta = a->AsApp()->rhs()->AsTuple();
  Tuple *tb = b->AsApp()->rhs()->AsTuple();
  if (EncodedVNameOrIdentLessThan(ta->element(0), tb->element(0))) {
    return true;
  }
  if (!EncodedVNameOrIdentEqualTo(ta->element(0), tb->element(0))) {
    return false;
  }
  if (EncodedIdentLessThan(ta->element(1), tb->element(1))) {
    return true;
  }
  if (!EncodedIdentEqualTo(ta->element(1), tb->element(1))) {
    return false;
  }
  if (EncodedVNameOrIdentLessThan(ta->element(2), tb->element(2))) {
    return true;
  }
  if (!EncodedVNameOrIdentEqualTo(ta->element(2), tb->element(2))) {
    return false;
  }
  if (EncodedIdentLessThan(ta->element(3), tb->element(3))) {
    return true;
  }
  if (!EncodedIdentEqualTo(ta->element(3), tb->element(3))) {
    return false;
  }
  if (EncodedIdentLessThan(ta->element(4), tb->element(4))) {
    return true;
  }
  return false;
}

static AstNode *DerefEVar(AstNode *node) {
  while (node) {
    if (auto *evar = node->AsEVar()) {
      node = evar->current();
    } else {
      break;
    }
  }
  return node;
}

static Identifier *SafeAsIdentifier(AstNode *node) {
  return node == nullptr ? nullptr : node->AsIdentifier();
}

struct AtomFactKey {
  Identifier *edge_kind;
  Identifier *fact_name;
  Identifier *fact_value;
  Identifier *source_vname[5] = {nullptr, nullptr, nullptr, nullptr, nullptr};
  Identifier *target_vname[5] = {nullptr, nullptr, nullptr, nullptr, nullptr};
  // fact_tuple is expected to be a full tuple from a Fact head
  AtomFactKey(AstNode *vname_head, Tuple *fact_tuple)
      : edge_kind(SafeAsIdentifier(DerefEVar(fact_tuple->element(1)))),
        fact_name(SafeAsIdentifier(DerefEVar(fact_tuple->element(3)))),
        fact_value(SafeAsIdentifier(DerefEVar(fact_tuple->element(4)))) {
    InitVNameFields(vname_head, fact_tuple->element(0), &source_vname[0]);
    InitVNameFields(vname_head, fact_tuple->element(2), &target_vname[0]);
  }
  void InitVNameFields(AstNode *vname_head, AstNode *maybe_vname,
                       Identifier **out) {
    maybe_vname = DerefEVar(maybe_vname);
    if (maybe_vname == nullptr) {
      return;
    }
    if (auto *app = maybe_vname->AsApp()) {
      if (DerefEVar(app->lhs()) != vname_head) {
        return;
      }
      AstNode *maybe_tuple = DerefEVar(app->rhs());
      if (maybe_tuple == nullptr) {
        return;
      }
      if (auto *tuple = maybe_tuple->AsTuple()) {
        if (tuple->size() != 5) {
          return;
        }
        for (size_t i = 0; i < 5; ++i) {
          out[i] = SafeAsIdentifier(DerefEVar(tuple->element(i)));
        }
      }
    }
  }
};

namespace {

enum class Order { LT, EQ, GT };

// How we order incomplete keys depends on whether we're looking for
// an upper or lower bound. See below for details. The node passed in
// must be an application of Fact to a full fact tuple.
static Order CompareFactWithKey(Order incomplete, AstNode *a, AtomFactKey *k) {
  Tuple *ta = a->AsApp()->rhs()->AsTuple();
  if (k->edge_kind == nullptr) {
    return incomplete;
  } else if (EncodedIdentLessThan(ta->element(1), k->edge_kind)) {
    return Order::LT;
  } else if (!EncodedIdentEqualTo(ta->element(1), k->edge_kind)) {
    return Order::GT;
  }
  if (k->fact_name == nullptr) {
    return incomplete;
  } else if (EncodedIdentLessThan(ta->element(3), k->fact_name)) {
    return Order::LT;
  } else if (!EncodedIdentEqualTo(ta->element(3), k->fact_name)) {
    return Order::GT;
  }
  if (k->fact_value == nullptr) {
    return incomplete;
  } else if (EncodedIdentLessThan(ta->element(4), k->fact_value)) {
    return Order::LT;
  } else if (!EncodedIdentEqualTo(ta->element(4), k->fact_value)) {
    return Order::GT;
  }
  auto vname_compare = [incomplete](Tuple *va, Identifier *tuple[5]) {
    for (size_t i = 0; i < 5; ++i) {
      if (tuple[i] == nullptr) {
        return incomplete;
      }
      if (EncodedIdentLessThan(va->element(i), tuple[i])) {
        return Order::LT;
      }
      if (!EncodedIdentEqualTo(va->element(i), tuple[i])) {
        return Order::GT;
      }
    }
    return Order::EQ;
  };
  if (Tuple *vs = ta->element(0)->AsApp()->rhs()->AsTuple()) {
    auto ord = vname_compare(vs, k->source_vname);
    if (ord != Order::EQ) {
      return ord;
    }
  }
  if (auto *app = ta->element(2)->AsApp()) {
    if (Tuple *vt = app->rhs()->AsTuple()) {
      auto ord = vname_compare(vt, k->target_vname);
      if (ord != Order::EQ) {
        return ord;
      }
    }
  }
  return Order::EQ;
}
}  // namespace

// We want to be able to find the following bounds:
// (0,0,2,3) (0,1,2,3) (0,1,2,4) (1,1,2,4)
//          ^---  (0,1,_,_)  ---^

static bool FastLookupKeyLessThanFact(AtomFactKey *k, AstNode *a) {
  // This is used to find upper bounds, so keys with incomplete suffixes should
  // be ordered after all facts that share their complete prefixes.
  return CompareFactWithKey(Order::LT, a, k) == Order::GT;
}

static bool FastLookupFactLessThanKey(AstNode *a, AtomFactKey *k) {
  // This is used to find lower bounds, so keys with incomplete suffixes should
  // be ordered after facts with lower prefixes but before facts with complete
  // suffixes.
  return CompareFactWithKey(Order::GT, a, k) == Order::LT;
}

//  Sort entries in lexicographic order, collating as:
// `(edge_kind, fact_name, fact_value, source_node, target_node)`.
// In practice most unification was happening between tuples
// with the first three fields present; then source_node
// missing some of the time; then target_node missing most of
// the time.
static bool FastLookupFactLessThan(AstNode *a, AstNode *b) {
  Tuple *ta = a->AsApp()->rhs()->AsTuple();
  Tuple *tb = b->AsApp()->rhs()->AsTuple();
  if (EncodedIdentLessThan(ta->element(1), tb->element(1))) {
    return true;
  }
  if (!EncodedIdentEqualTo(ta->element(1), tb->element(1))) {
    return false;
  }
  if (EncodedIdentLessThan(ta->element(3), tb->element(3))) {
    return true;
  }
  if (!EncodedIdentEqualTo(ta->element(3), tb->element(3))) {
    return false;
  }
  if (EncodedIdentLessThan(ta->element(4), tb->element(4))) {
    return true;
  }
  if (!EncodedIdentEqualTo(ta->element(4), tb->element(4))) {
    return false;
  }
  if (EncodedVNameOrIdentLessThan(ta->element(0), tb->element(0))) {
    return true;
  }
  if (!EncodedVNameOrIdentEqualTo(ta->element(0), tb->element(0))) {
    return false;
  }
  if (EncodedVNameOrIdentLessThan(ta->element(2), tb->element(2))) {
    return true;
  }
  return false;
}

// The Solver acts in a closed world: any universal quantification can be
// exhaustively tested against database facts.
// Based on _A Semi-Functional Implementation of a Higher-Order Logic
// Programming Language_ by Conal Elliott and Frank Pfenning (draft of
// February 1990).
// It is not our intention to build a particularly performant or complete
// inference engine. If the solver starts to get too hairy we might want to
// look at deferring to a pre-existing system.
class Solver {
 public:
  using Inspection = AssertionParser::Inspection;

  Solver(Verifier *context, Database &database,
         std::multimap<std::pair<size_t, size_t>, AstNode *> &anchors,
         std::function<bool(Verifier *, const Inspection &)> &inspect)
      : context_(*context),
        database_(database),
        anchors_(anchors),
        inspect_(inspect) {}

  ThunkRet UnifyTuple(Tuple *st, Tuple *tt, size_t ofs, size_t max,
                      ThunkRet cut, Thunk f) {
    if (ofs == max) return f();
    return Unify(st->element(ofs), tt->element(ofs), cut,
                 [this, st, tt, ofs, max, cut, &f]() {
                   return UnifyTuple(st, tt, ofs + 1, max, cut, f);
                 });
  }

  ThunkRet Unify(AstNode *s, AstNode *t, ThunkRet cut, Thunk f) {
    if (EVar *e = s->AsEVar()) {
      return UnifyEVar(e, t, cut, f);
    } else if (EVar *e = t->AsEVar()) {
      return UnifyEVar(e, s, cut, f);
    } else if (Identifier *si = s->AsIdentifier()) {
      if (Identifier *ti = t->AsIdentifier()) {
        if (si->symbol() == ti->symbol()) {
          return f();
        }
      }
    } else if (App *sa = s->AsApp()) {
      if (App *ta = t->AsApp()) {
        return Unify(sa->lhs(), ta->lhs(), cut, [this, sa, ta, cut, &f]() {
          return Unify(sa->rhs(), ta->rhs(), cut, f);
        });
      }
    } else if (Tuple *st = s->AsTuple()) {
      if (Tuple *tt = t->AsTuple()) {
        if (st->size() != tt->size()) {
          return kNoException;
        }
        return UnifyTuple(st, tt, 0, st->size(), cut, f);
      }
    } else if (Range *sr = s->AsRange()) {
      if (Range *tr = t->AsRange()) {
        if (sr->begin() == tr->begin() && sr->end() == tr->end()) {
          return f();
        }
      }
    }
    return kNoException;
  }

  bool Occurs(EVar *e, AstNode *t) {
    if (App *a = t->AsApp()) {
      return Occurs(e, a->lhs()) || Occurs(e, a->rhs());
    } else if (EVar *ev = t->AsEVar()) {
      return ev->current() ? Occurs(e, ev->current()) : e == ev;
    } else if (Tuple *tu = t->AsTuple()) {
      for (size_t i = 0, c = tu->size(); i != c; ++i) {
        if (Occurs(e, tu->element(i))) {
          return true;
        }
      }
      return false;
    } else if (Range *r = t->AsRange()) {
      return false;
    } else {
      CHECK(t->AsIdentifier() && "Inexhaustive match.");
      return false;
    }
    return true;
  }

  ThunkRet UnifyEVar(EVar *e, AstNode *t, ThunkRet cut, Thunk f) {
    if (AstNode *ec = e->current()) {
      return Unify(ec, t, cut, f);
    }
    if (t->AsEVar() == e) {
      return f();
    }
    if (Occurs(e, t)) {
      FileHandlePrettyPrinter printer(stderr);
      printer.Print("Detected a cycle involving ");
      e->Dump(*context_.symbol_table(), &printer);
      printer.Print(" while unifying it with ");
      t->Dump(*context_.symbol_table(), &printer);
      printer.Print(".\n");
      return kInvalidProgram;
    }
    e->set_current(t);
    ThunkRet f_ret = f();
    if (f_ret != cut) {
      e->set_current(nullptr);
    }
    return f_ret;
  }

  ThunkRet MatchAtomVersusDatabase(AstNode *atom, ThunkRet cut, Thunk f) {
    if (auto *app = atom->AsApp()) {
      if (app->lhs() == context_.fact_id()) {
        if (auto *tuple = app->rhs()->AsTuple()) {
          if (tuple->size() == 5) {
            AtomFactKey key(context_.vname_id(), tuple);
            // Make use of the fast lookup sort order.
            auto begin = std::lower_bound(database_.begin(), database_.end(),
                                          &key, FastLookupFactLessThanKey);
            auto end = std::upper_bound(database_.begin(), database_.end(),
                                        &key, FastLookupKeyLessThanFact);
            for (auto i = begin; i != end; ++i) {
              ThunkRet exc = Unify(atom, *i, cut, f);
              if (exc != kNoException) {
                return exc;
              }
            }
            return kNoException;
          }
        }
      }
    }
    // Not enough information to filter by.
    for (size_t fact = 0; fact < database_.size(); ++fact) {
      ThunkRet exc = Unify(atom, database_[fact], cut, f);
      if (exc != kNoException) {
        return exc;
      }
    }
    return kNoException;
  }

  /// \brief If `atom` has the syntactic form =(a, b), returns the tuple (a, b).
  /// Otherwise returns `null`.
  Tuple *MatchEqualsArgs(AstNode *atom) {
    if (App *a = atom->AsApp()) {
      if (Identifier *id = a->lhs()->AsIdentifier()) {
        if (id->symbol() == context_.eq_id()->symbol()) {
          if (Tuple *tu = a->rhs()->AsTuple()) {
            if (tu->size() == 2) {
              return tu;
            }
          }
        }
      }
    }
    return nullptr;
  }

  ThunkRet MatchAtom(AstNode *atom, AstNode *program, ThunkRet cut, Thunk f) {
    // We only have the database and eq-constraints right now.
    assert(program == nullptr);
    if (auto *tu = MatchEqualsArgs(atom)) {
      if (Range *r = tu->element(0)->AsRange()) {
        auto anchors =
            anchors_.equal_range(std::make_pair(r->begin(), r->end()));
        if (anchors.first == anchors.second) {
          // There's no anchor with this range in the database.
          // This goal can therefore never succeed.
          return kImpossible;
        }
        for (auto anchor = anchors.first; anchor != anchors.second; ++anchor) {
          ThunkRet unify_ret = Unify(anchor->second, tu->element(1), cut, f);
          if (unify_ret != kNoException) {
            return unify_ret;
          }
        }
        return kNoException;
      }
      // =(a, b) succeeds if unify(a, b) succeeds.
      return Unify(tu->element(0), tu->element(1), cut, f);
    }
    return MatchAtomVersusDatabase(atom, cut, f);
  }

  ThunkRet SolveGoal(AstNode *goal, ThunkRet cut, Thunk f) {
    // We only have atomic goals right now.
    if (App *a = goal->AsApp()) {
      return MatchAtom(goal, nullptr, cut, f);
    } else {
      // TODO(zarko): Replace with a configurable PrettyPrinter.
      LOG(ERROR) << "Invalid AstNode in goal-expression.";
      return kInvalidProgram;
    }
  }

  ThunkRet SolveGoalArray(AssertionParser::GoalGroup *group, size_t cur,
                          ThunkRet cut, Thunk f) {
    if (cur > highest_goal_reached_) {
      highest_goal_reached_ = cur;
    }
    if (cur == group->goals.size()) {
      return f();
    }
    return SolveGoal(group->goals[cur], cut, [this, group, cur, cut, &f]() {
      return SolveGoalArray(group, cur + 1, cut, f);
    });
  }

  bool PerformInspection() {
    for (const auto &inspection : context_.parser()->inspections()) {
      if (!inspect_(&context_, inspection)) {
        return false;
      }
    }
    return true;
  }

  ThunkRet SolveGoalGroups(AssertionParser *context, Thunk f) {
    for (size_t cur = 0, cut = kFirstCut; cur < context->groups().size();
         ++cur, ++cut) {
      auto *group = &context->groups()[cur];
      if (cur > highest_group_reached_) {
        highest_goal_reached_ = 0;
        highest_group_reached_ = cur;
      }
      ThunkRet result = SolveGoalArray(
          group, 0, cut,
          [this, context, group, cur, cut, &f]() { return cut; });
      // Lots of unwinding later...
      if (result == cut) {
        // That last goal group succeeded.
        if (group->accept_if != AssertionParser::GoalGroup::kNoneMayFail) {
          return PerformInspection() ? kNoException : kInvalidProgram;
        }
      } else if (result == kNoException || result == kImpossible) {
        // That last goal group failed.
        if (group->accept_if != AssertionParser::GoalGroup::kSomeMustFail) {
          return PerformInspection() ? kNoException : kInvalidProgram;
        }
      } else {
        return result;
      }
    }
    return PerformInspection() ? f() : kInvalidProgram;
  }

  bool Solve() {
    ThunkRet exn = SolveGoalGroups(context_.parser(), []() { return kSolved; });
    return exn == kSolved;
  }

  size_t highest_group_reached() const { return highest_group_reached_; }

  size_t highest_goal_reached() const { return highest_goal_reached_; }

 private:
  Verifier &context_;
  Database &database_;
  std::multimap<std::pair<size_t, size_t>, AstNode *> &anchors_;
  std::function<bool(Verifier *, const Inspection &)> &inspect_;
  size_t highest_group_reached_ = 0;
  size_t highest_goal_reached_ = 0;
};
}  // anonymous namespace

Verifier::Verifier(bool trace_lex, bool trace_parse)
    : parser_(this, trace_lex, trace_parse),
      builtin_location_name_("builtins") {
  builtin_location_.initialize(&builtin_location_name_);
  builtin_location_.begin.column = 1;
  builtin_location_.end.column = 1;
  empty_string_id_ = IdentifierFor(builtin_location_, "");
  fact_id_ = IdentifierFor(builtin_location_, "fact");
  vname_id_ = IdentifierFor(builtin_location_, "vname");
  kind_id_ = IdentifierFor(builtin_location_, "/kythe/node/kind");
  anchor_id_ = IdentifierFor(builtin_location_, "anchor");
  start_id_ = IdentifierFor(builtin_location_, "/kythe/loc/start");
  end_id_ = IdentifierFor(builtin_location_, "/kythe/loc/end");
  root_id_ = IdentifierFor(builtin_location_, "/");
  eq_id_ = IdentifierFor(builtin_location_, "=");
  ordinal_id_ = IdentifierFor(builtin_location_, "/kythe/ordinal");
  file_id_ = IdentifierFor(builtin_location_, "file");
  text_id_ = IdentifierFor(builtin_location_, "/kythe/text");
  code_id_ = IdentifierFor(builtin_location_, "/kythe/code");
  marked_source_child_id_ =
      IdentifierFor(builtin_location_, "/kythe/edge/child");
  marked_source_box_id_ = IdentifierFor(builtin_location_, "BOX");
  marked_source_type_id_ = IdentifierFor(builtin_location_, "TYPE");
  marked_source_parameter_id_ = IdentifierFor(builtin_location_, "PARAMETER");
  marked_source_identifier_id_ = IdentifierFor(builtin_location_, "IDENTIFIER");
  marked_source_context_id_ = IdentifierFor(builtin_location_, "CONTEXT");
  marked_source_initializer_id_ =
      IdentifierFor(builtin_location_, "INITIALIZER");
  marked_source_parameter_lookup_by_param_id_ =
      IdentifierFor(builtin_location_, "PARAMETER_LOOKUP_BY_PARAM");
  marked_source_lookup_by_param_id_ =
      IdentifierFor(builtin_location_, "LOOKUP_BY_PARAM");
  marked_source_parameter_lookup_by_param_with_defaults_id_ = IdentifierFor(
      builtin_location_, "PARAMETER_LOOKUP_BY_PARAM_WITH_DEFAULTS");
  marked_source_kind_id_ = IdentifierFor(builtin_location_, "/kythe/kind");
  marked_source_pre_text_id_ =
      IdentifierFor(builtin_location_, "/kythe/pre_text");
  marked_source_post_child_text_id_ =
      IdentifierFor(builtin_location_, "/kythe/post_child_text");
  marked_source_post_text_id_ =
      IdentifierFor(builtin_location_, "/kythe/post_text");
  marked_source_lookup_index_id_ =
      IdentifierFor(builtin_location_, "/kythe/lookup_index");
  marked_source_default_children_count_id_ =
      IdentifierFor(builtin_location_, "/kythe/default_children_count");
  marked_source_add_final_list_token_id_ =
      IdentifierFor(builtin_location_, "/kythe/add_final_list_token");
  marked_source_link_id_ = IdentifierFor(builtin_location_, "/kythe/edge/link");
  marked_source_true_id_ = IdentifierFor(builtin_location_, "true");
  marked_source_code_edge_id_ =
      IdentifierFor(builtin_location_, "/kythe/edge/code");
  marked_source_false_id_ = IdentifierFor(builtin_location_, "false");
  SetGoalCommentPrefix("//-");
}

void Verifier::SetGoalCommentPrefix(const std::string &it) {
  std::string error;
  auto escaped = RE2::QuoteMeta(it);
  CHECK(SetGoalCommentRegex("\\s*" + escaped + "(.*)", &error)) << error;
}

bool Verifier::SetGoalCommentRegex(const std::string &regex,
                                   std::string *error) {
  auto re2 = std::unique_ptr<RE2>(new RE2(regex));
  if (re2->error_code() != RE2::NoError) {
    if (error) {
      *error = re2->error();
      return false;
    }
  }
  if (re2->NumberOfCapturingGroups() != 1) {
    if (error) {
      *error = "Wrong number of capture groups in goal comment regex ";
      // This is useful to show, since the shell might unexpectedly shred
      // regexes.
      error->append(regex);
      error->append("(want 1).");
      return false;
    }
  }
  goal_comment_regex_ = std::move(re2);
  return true;
}

bool Verifier::LoadInlineProtoFile(const std::string &file_data) {
  kythe::proto::Entries entries;
  bool ok = google::protobuf::TextFormat::ParseFromString(file_data, &entries);
  if (!ok) {
    // TODO(zarko): Replace with a configurable PrettyPrinter.
    LOG(ERROR) << "Unable to parse text protobuf.";
    return false;
  }
  for (int i = 0; i < entries.entries_size(); ++i) {
    if (!AssertSingleFact(kDefaultDatabase, i, entries.entries(i))) {
      return false;
    }
  }
  return parser_.ParseInlineRuleString(file_data, *kStandardIn,
                                       "\\s*\\#\\-(.*)");
}

bool Verifier::LoadInlineRuleFile(const std::string &filename) {
  bool parsed = parser_.ParseInlineRuleFile(filename, *goal_comment_regex_);
  if (!parsed) {
    return false;
  }
  return true;
}

bool Verifier::LoadInMemoryRuleFile(AstNode *vname, Symbol text) {
  StringPrettyPrinter printer;
  vname->Dump(symbol_table_, &printer);
  fake_files_[printer.str()] = text;
  return parser_.ParseInlineRuleString(symbol_table_.text(text), printer.str(),
                                       *goal_comment_regex_);
}

void Verifier::IgnoreDuplicateFacts() { ignore_dups_ = true; }

void Verifier::SaveEVarAssignments() {
  saving_assignments_ = true;
  parser_.InspectAllEVars();
}

void Verifier::ShowGoals() {
  FileHandlePrettyPrinter printer(stdout);
  for (auto &group : parser_.groups()) {
    if (group.accept_if == AssertionParser::GoalGroup::kNoneMayFail) {
      printer.Print("group:\n");
    } else {
      printer.Print("negated group:\n");
    }
    for (auto *goal : group.goals) {
      printer.Print("  goal: ");
      goal->Dump(symbol_table_, &printer);
      printer.Print("\n");
    }
  }
}

static bool PrintInMemoryFileSection(const std::string &file_text,
                                     size_t start_line, size_t start_ix,
                                     size_t end_line, size_t end_ix,
                                     PrettyPrinter *printer) {
  size_t current_line = 0;
  size_t pos = 0;
  auto walk_lines = [&](size_t until_line) {
    if (until_line == current_line) {
      return pos;
    }
    do {
      auto endline = file_text.find('\n', pos);
      if (endline == std::string::npos) {
        return std::string::npos;
      }
      pos = endline + 1;
    } while (++current_line < start_line);
    return pos;
  };
  auto begin = walk_lines(start_line);
  auto end = begin == std::string::npos ? begin : walk_lines(end_line);
  auto begin_ofs = begin + start_ix;
  auto end_ofs = end + end_ix;
  if (begin == std::string::npos || end == std::string::npos ||
      begin_ofs > file_text.size() || end_ofs > file_text.size()) {
    printer->Print("(error line out of bounds)");
    return false;
  }
  printer->Print(file_text.substr(begin_ofs, end_ofs - begin_ofs));
  return true;
}

static bool PrintFileSection(FILE *file, size_t start_line, size_t start_ix,
                             size_t end_line, size_t end_ix,
                             PrettyPrinter *printer) {
  if (!file) {
    printer->Print("(null file)\n");
    return false;
  }
  char *lineptr = nullptr;
  size_t buf_length = 0;
  ssize_t line_length = 0;
  size_t line_number = 0;
  while ((line_length = getline(&lineptr, &buf_length, file)) != -1) {
    if (line_number >= start_line && line_number <= end_line) {
      std::string text(lineptr);
      size_t line_begin = 0, line_end = text.size();
      if (line_number == start_line) {
        line_begin = start_ix;
      }
      if (line_number == end_line) {
        line_end = end_ix;
      }
      if (line_end - line_begin > text.size()) {
        printer->Print("(error line too big for actual line)\n");
      } else {
        text = text.substr(line_begin, line_end - line_begin);
        printer->Print(text);
      }
    }
    if (line_number == end_line) {
      free(lineptr);
      return true;
    }
    ++line_number;
  }
  printer->Print("(error line out of bounds)\n");
  free(lineptr);
  return false;
}

void Verifier::DumpErrorGoal(size_t group, size_t index) {
  FileHandlePrettyPrinter printer(stderr);
  if (group >= parser_.groups().size()) {
    printer.Print("(invalid group index ");
    printer.Print(std::to_string(group));
    printer.Print(")\n");
  }
  if (index >= parser_.groups()[group].goals.size()) {
    if (index > parser_.groups()[group].goals.size() ||
        parser_.groups()[group].goals.empty()) {
      printer.Print("(invalid index ");
      printer.Print(std::to_string(group));
      printer.Print(":");
      printer.Print(std::to_string(index));
      printer.Print(")\n");
      return;
    }
    printer.Print("(past the end of a ");
    if (parser_.groups()[group].accept_if ==
        AssertionParser::GoalGroup::kSomeMustFail) {
      printer.Print("negated ");
    }
    printer.Print("group, whose last goal was)\n  ");
    --index;
  }
  auto *goal = parser_.groups()[group].goals[index];
  yy::location goal_location = goal->location();
  yy::position goal_begin = goal_location.begin;
  yy::position goal_end = goal_location.end;
  if (goal_end.filename) {
    printer.Print(*goal_end.filename);
  } else {
    printer.Print("-");
  }
  printer.Print(":");
  if (goal_begin.filename) {
    printer.Print(std::to_string(goal_begin.line) + ":" +
                  std::to_string(goal_begin.column));
  }
  printer.Print("-");
  if (goal_end.filename) {
    printer.Print(std::to_string(goal_end.line) + ":" +
                  std::to_string(goal_end.column));
  }
  bool printed_goal = false;
  printer.Print(" ");
  if (goal_end.filename) {
    auto has_symbol = fake_files_.find(*goal_end.filename);
    if (has_symbol != fake_files_.end()) {
      printed_goal = PrintInMemoryFileSection(
          symbol_table_.text(has_symbol->second), goal_begin.line - 1,
          goal_begin.column - 1, goal_end.line - 1, goal_end.column - 1,
          &printer);
    } else if (*goal_end.filename != *kStandardIn &&
               *goal_begin.filename == *goal_end.filename) {
      FILE *f = fopen(goal_end.filename->c_str(), "r");
      if (f != nullptr) {
        printed_goal =
            PrintFileSection(f, goal_begin.line - 1, goal_begin.column - 1,
                             goal_end.line - 1, goal_end.column - 1, &printer);
        fclose(f);
      }
    }
  }
  printer.Print("\n  Goal: ");
  goal->Dump(symbol_table_, &printer);
  printer.Print("\n");
}

bool Verifier::VerifyAllGoals(
    std::function<bool(Verifier *, const Solver::Inspection &)> inspect) {
  if (!PrepareDatabase()) {
    return false;
  }
  Solver solver(this, facts_, anchors_, inspect);
  bool result = solver.Solve();
  highest_goal_reached_ = solver.highest_goal_reached();
  highest_group_reached_ = solver.highest_group_reached();
  return result;
}

bool Verifier::VerifyAllGoals() {
  return VerifyAllGoals([this](Verifier *context,
                               const Solver::Inspection &inspection) {
    if (inspection.kind == Solver::Inspection::Kind::EXPLICIT) {
      FileHandlePrettyPrinter printer(saving_assignments_ ? stderr : stdout);
      printer.Print(inspection.label);
      printer.Print(": ");
      inspection.evar->Dump(symbol_table_, &printer);
      printer.Print("\n");
    }
    if (inspection.evar->current()) {
      saved_assignments_[inspection.label] = inspection.evar->current();
    }
    return true;
  });
}

Identifier *Verifier::IdentifierFor(const yy::location &location,
                                    const std::string &token) {
  Symbol symbol = symbol_table_.intern(token);
  return new (&arena_) Identifier(location, symbol);
}

Identifier *Verifier::IdentifierFor(const yy::location &location, int integer) {
  Symbol symbol = symbol_table_.intern(std::to_string(integer));
  return new (&arena_) Identifier(location, symbol);
}

AstNode *Verifier::MakePredicate(const yy::location &location, AstNode *head,
                                 std::initializer_list<AstNode *> values) {
  size_t values_count = values.size();
  AstNode **body = (AstNode **)arena_.New(values_count * sizeof(AstNode *));
  size_t vn = 0;
  for (AstNode *v : values) {
    body[vn] = v;
    ++vn;
  }
  AstNode *tuple = new (&arena_) Tuple(location, values_count, body);
  return new (&arena_) App(location, head, tuple);
}

/// \brief Sort nodes such that nodes and facts are grouped.
static bool GraphvizSortOrder(AstNode *a, AstNode *b) {
  Tuple *ta = a->AsApp()->rhs()->AsTuple();
  Tuple *tb = b->AsApp()->rhs()->AsTuple();
  if (EncodedVNameOrIdentLessThan(ta->element(0), tb->element(0))) {
    return true;
  }
  if (!EncodedVNameOrIdentEqualTo(ta->element(0), tb->element(0))) {
    return false;
  }
  if (EncodedIdentLessThan(ta->element(1), tb->element(1))) {
    return true;
  }
  if (!EncodedIdentEqualTo(ta->element(1), tb->element(1))) {
    return false;
  }
  if (EncodedVNameOrIdentLessThan(ta->element(2), tb->element(2))) {
    return true;
  }
  if (!EncodedVNameOrIdentEqualTo(ta->element(2), tb->element(2))) {
    return false;
  }
  if (EncodedIdentLessThan(ta->element(3), tb->element(3))) {
    return true;
  }
  if (!EncodedIdentEqualTo(ta->element(3), tb->element(3))) {
    return false;
  }
  if (EncodedIdentLessThan(ta->element(4), tb->element(4))) {
    return true;
  }
  return false;
}

static bool EncodedFactEqualTo(AstNode *a, AstNode *b) {
  Tuple *ta = a->AsApp()->rhs()->AsTuple();
  Tuple *tb = b->AsApp()->rhs()->AsTuple();
  return EncodedVNameOrIdentEqualTo(ta->element(0), tb->element(0)) &&
         EncodedIdentEqualTo(ta->element(1), tb->element(1)) &&
         EncodedVNameOrIdentEqualTo(ta->element(2), tb->element(2)) &&
         EncodedIdentEqualTo(ta->element(3), tb->element(3)) &&
         EncodedIdentEqualTo(ta->element(4), tb->element(4));
}

static bool EncodedVNameHasValidForm(Verifier *cxt, AstNode *a) {
  Tuple *ta = a->AsApp()->rhs()->AsTuple();
  return ta->element(0) != cxt->empty_string_id() ||
         ta->element(1) != cxt->empty_string_id() ||
         ta->element(2) != cxt->empty_string_id() ||
         ta->element(3) != cxt->empty_string_id() ||
         ta->element(4) != cxt->empty_string_id();
}

static bool EncodedFactHasValidForm(Verifier *cxt, AstNode *a) {
  Tuple *ta = a->AsApp()->rhs()->AsTuple();
  if (ta->element(0) == cxt->empty_string_id() ||
      !EncodedVNameHasValidForm(cxt, ta->element(0))) {
    // Always need a source.
    return false;
  }
  if (ta->element(1) == cxt->empty_string_id()) {
    // (source, "", "", string, _)
    return ta->element(2) == cxt->empty_string_id() &&
           ta->element(3) != cxt->empty_string_id();
  } else {
    // (source, edge, target, ...
    if (ta->element(2) == cxt->empty_string_id() ||
        !EncodedVNameHasValidForm(cxt, ta->element(2))) {
      return false;
    }
    if (EncodedIdentEqualTo(ta->element(3), cxt->root_id())) {
      // ... /, )
      return EncodedIdentEqualTo(ta->element(4), cxt->empty_string_id());
    } else {
      // ... /kythe/ordinal, base10string )
      if (!EncodedIdentEqualTo(ta->element(3), cxt->ordinal_id())) {
        return false;
      }
      const std::string &ordinal_val =
          cxt->symbol_table()->text(ta->element(4)->AsIdentifier()->symbol());
      // TODO: check if valid int
      return true;
    }
  }
}

bool Verifier::PrepareDatabase() {
  if (database_prepared_) {
    return true;
  }
  // TODO(zarko): Make this configurable.
  FileHandlePrettyPrinter printer(stderr);
  // First, sort the tuples. As an invariant, we know they will be of the form
  // fact (vname | ident, ident, vname | ident, ident, ident)
  // vname (ident, ident, ident, ident, ident)
  // and all idents will have been uniqued (so we can compare them purely
  // by symbol ID).
  std::sort(facts_.begin(), facts_.end(), EncodedFactLessThan);
  // Now we can do a simple pairwise check on each of the facts to see
  // whether the invariants hold.
  bool is_ok = true;
  AstNode *last_anchor_vname = nullptr;
  AstNode *last_file_vname = nullptr;
  size_t last_anchor_start = ~0;
  for (size_t f = 0; f < facts_.size(); ++f) {
    AstNode *fb = facts_[f];

    if (!EncodedFactHasValidForm(this, fb)) {
      printer.Print("Fact has invalid form:\n  ");
      fb->Dump(symbol_table_, &printer);
      printer.Print("\n");
      is_ok = false;
      continue;
    }
    Tuple *tb = fb->AsApp()->rhs()->AsTuple();
    if (tb->element(1) == empty_string_id_ &&
        tb->element(2) == empty_string_id_) {
      bool is_kind_fact = EncodedIdentEqualTo(tb->element(3), kind_id_);
      // Check to see if this fact entry describes part of a file.
      // NB: kind_id_ is ordered before text_id_.
      if (assertions_from_file_nodes_) {
        if (is_kind_fact) {
          if (EncodedIdentEqualTo(tb->element(4), file_id_)) {
            last_file_vname = tb->element(0);
          } else {
            last_file_vname = nullptr;
          }
        } else if (last_file_vname != nullptr &&
                   EncodedIdentEqualTo(tb->element(3), text_id_)) {
          if (EncodedVNameOrIdentEqualTo(last_file_vname, tb->element(0))) {
            if (!LoadInMemoryRuleFile(
                    tb->element(0), tb->element(4)->AsIdentifier()->symbol())) {
              is_ok = false;
            }
          }
          last_file_vname = nullptr;
        }
      }
      // Check to see if this fact entry describes part of an anchor.
      // We've arranged via EncodedFactLessThan to sort kind_id_ before
      // start_id_ and start_id_ before end_id_ and to group all node facts
      // together in uninterrupted runs.
      if (is_kind_fact && EncodedIdentEqualTo(tb->element(4), anchor_id_)) {
        // Start tracking a new anchor.
        last_anchor_vname = tb->element(0);
        last_anchor_start = ~0;
      } else if (last_anchor_vname != nullptr &&
                 EncodedIdentEqualTo(tb->element(3), start_id_) &&
                 tb->element(4)->AsIdentifier()) {
        if (EncodedVNameOrIdentEqualTo(last_anchor_vname, tb->element(0))) {
          // This is a fact about the anchor we're tracking.
          std::stringstream(
              symbol_table_.text(tb->element(4)->AsIdentifier()->symbol())) >>
              last_anchor_start;
        } else {
          // This is a fact about node we're not tracking; given our sort order,
          // we'll never get enough information for the node we are tracking,
          // so stop tracking it.
          last_anchor_vname = nullptr;
          last_anchor_start = ~0;
        }
      } else if (last_anchor_start != ~0 &&
                 EncodedIdentEqualTo(tb->element(3), end_id_) &&
                 tb->element(4)->AsIdentifier()) {
        if (EncodedVNameOrIdentEqualTo(last_anchor_vname, tb->element(0))) {
          // We have enough information about the anchor we're tracking.
          size_t last_anchor_end = ~0;
          std::stringstream(
              symbol_table_.text(tb->element(4)->AsIdentifier()->symbol())) >>
              last_anchor_end;
          AddAnchor(last_anchor_vname, last_anchor_start, last_anchor_end);
        }
        last_anchor_vname = nullptr;
        last_anchor_start = ~0;
      }
    }
    if (f == 0) {
      continue;
    }

    AstNode *fa = facts_[f - 1];
    if (!ignore_dups_ && EncodedFactEqualTo(fa, fb)) {
      printer.Print("Two facts were equal:\n  ");
      fa->Dump(symbol_table_, &printer);
      printer.Print("\n  ");
      fb->Dump(symbol_table_, &printer);
      printer.Print("\n");
      is_ok = false;
      continue;
    }
    Tuple *ta = fa->AsApp()->rhs()->AsTuple();
    if (EncodedVNameEqualTo(ta->element(0)->AsApp(), tb->element(0)->AsApp()) &&
        ta->element(1) == empty_string_id_ &&
        tb->element(1) == empty_string_id_ &&
        ta->element(2) == empty_string_id_ &&
        tb->element(2) == empty_string_id_ &&
        EncodedIdentEqualTo(ta->element(3), tb->element(3)) &&
        !EncodedIdentEqualTo(ta->element(4), tb->element(4))) {
      if (EncodedIdentEqualTo(ta->element(3), code_id_)) {
        // TODO(zarko): Add documentation for these new edges (T195).
        printer.Print(
            "Two /kythe/code facts about a node differed in value:\n  ");
        ta->element(0)->Dump(symbol_table_, &printer);
        printer.Print("\n  ");
        printer.Print("\nThe decoded values were:\n");
        auto print_decoded = [&](AstNode *value) {
          if (auto *ident = value->AsIdentifier()) {
            proto::common::MarkedSource marked_source;
            if (!marked_source.ParseFromString(
                    symbol_table_.text(ident->symbol()))) {
              printer.Print("(failed to decode)\n");
            } else {
              printer.Print(marked_source.DebugString());
              printer.Print("\n");
            }
          } else {
            printer.Print("(not an identifier)\n");
          }
        };
        print_decoded(ta->element(4));
        printer.Print("\n -----------------  versus  ----------------- \n\n");
        print_decoded(tb->element(4));
      } else {
        printer.Print("Two facts about a node differed in value:\n  ");
        fa->Dump(symbol_table_, &printer);
        printer.Print("\n  ");
        fb->Dump(symbol_table_, &printer);
        printer.Print("\n");
      }
      is_ok = false;
    }
  }
  if (is_ok) {
    std::sort(facts_.begin(), facts_.end(), FastLookupFactLessThan);
  }
  database_prepared_ = is_ok;
  return is_ok;
}

AstNode *Verifier::ConvertVName(const yy::location &loc,
                                const kythe::proto::VName &vname) {
  AstNode **values = (AstNode **)arena_.New(sizeof(AstNode *) * 5);
  values[0] = vname.signature().empty() ? empty_string_id_
                                        : IdentifierFor(loc, vname.signature());
  values[1] = vname.corpus().empty() ? empty_string_id_
                                     : IdentifierFor(loc, vname.corpus());
  values[2] = vname.root().empty() ? empty_string_id_
                                   : IdentifierFor(loc, vname.root());
  values[3] = vname.path().empty() ? empty_string_id_
                                   : IdentifierFor(loc, vname.path());
  values[4] = vname.language().empty() ? empty_string_id_
                                       : IdentifierFor(loc, vname.language());
  AstNode *tuple = new (&arena_) Tuple(loc, 5, values);
  return new (&arena_) App(vname_id_, tuple);
}

AstNode *Verifier::NewUniqueVName(const yy::location &loc) {
  return MakePredicate(
      loc, vname_id_,
      {new (&arena_) Identifier(loc, symbol_table_.unique()), empty_string_id_,
       empty_string_id_, empty_string_id_, empty_string_id_});
}

AstNode *Verifier::ConvertCodeFact(const yy::location &loc,
                                   const google::protobuf::string &code_data) {
  proto::common::MarkedSource marked_source;
  if (!marked_source.ParseFromString(code_data)) {
    std::cerr << loc << ": can't parse code protobuf" << std::endl;
    return nullptr;
  }
  return ConvertMarkedSource(loc, marked_source);
}

AstNode *Verifier::ConvertMarkedSource(const yy::location &loc,
                                       const proto::common::MarkedSource &source) {
  // Explode each MarkedSource message into a node with an unutterable vname.
  auto *vname = NewUniqueVName(loc);
  for (int child = 0; child < source.child_size(); ++child) {
    auto *child_vname = ConvertMarkedSource(loc, source.child(child));
    if (child_vname == nullptr) {
      return nullptr;
    }
    facts_.push_back(MakePredicate(
        loc, fact_id_,
        {vname, marked_source_child_id_, child_vname, ordinal_id_,
         IdentifierFor(builtin_location_, std::to_string(child))}));
  }
  for (const auto &link : source.link()) {
    if (link.definition_size() != 1) {
      std::cerr << loc << ": bad link: want one definition" << std::endl;
      return nullptr;
    }
    auto from_uri = URI::FromString(link.definition(0));
    if (!from_uri.first) {
      std::cerr << loc << ": bad URI in link" << std::endl;
      return nullptr;
    }
    facts_.push_back(MakePredicate(loc, fact_id_,
                                   {vname, marked_source_link_id_,
                                    ConvertVName(loc, from_uri.second.v_name()),
                                    root_id_, empty_string_id_}));
  }
  auto emit_fact = [&](AstNode *fact_id, AstNode *fact_value) {
    facts_.push_back(MakePredicate(
        loc, fact_id_,
        {vname, empty_string_id_, empty_string_id_, fact_id, fact_value}));
  };
  switch (source.kind()) {
    case proto::common::MarkedSource::BOX:
      emit_fact(marked_source_kind_id_, marked_source_box_id_);
      break;
    case proto::common::MarkedSource::TYPE:
      emit_fact(marked_source_kind_id_, marked_source_type_id_);
      break;
    case proto::common::MarkedSource::PARAMETER:
      emit_fact(marked_source_kind_id_, marked_source_parameter_id_);
      break;
    case proto::common::MarkedSource::IDENTIFIER:
      emit_fact(marked_source_kind_id_, marked_source_identifier_id_);
      break;
    case proto::common::MarkedSource::CONTEXT:
      emit_fact(marked_source_kind_id_, marked_source_context_id_);
      break;
    case proto::common::MarkedSource::INITIALIZER:
      emit_fact(marked_source_kind_id_, marked_source_initializer_id_);
      break;
    case proto::common::MarkedSource::PARAMETER_LOOKUP_BY_PARAM:
      emit_fact(marked_source_kind_id_,
                marked_source_parameter_lookup_by_param_id_);
      break;
    case proto::common::MarkedSource::LOOKUP_BY_PARAM:
      emit_fact(marked_source_kind_id_, marked_source_lookup_by_param_id_);
      break;
    case proto::common::MarkedSource::PARAMETER_LOOKUP_BY_PARAM_WITH_DEFAULTS:
      emit_fact(marked_source_kind_id_,
                marked_source_parameter_lookup_by_param_with_defaults_id_);
      break;
    // The proto enum is polluted with enumerators like
    // MarkedSource_Kind_MarkedSource_Kind_INT_MIN_SENTINEL_DO_NOT_USE_.
    default:
      std::cerr << loc << ": unknown source kind for MarkedSource" << std::endl;
  }
  emit_fact(marked_source_pre_text_id_, IdentifierFor(loc, source.pre_text()));
  emit_fact(marked_source_post_child_text_id_,
            IdentifierFor(loc, source.post_child_text()));
  emit_fact(marked_source_post_text_id_,
            IdentifierFor(loc, source.post_text()));
  emit_fact(marked_source_lookup_index_id_,
            IdentifierFor(loc, std::to_string(source.lookup_index())));
  emit_fact(
      marked_source_default_children_count_id_,
      IdentifierFor(loc, std::to_string(source.default_children_count())));
  emit_fact(marked_source_add_final_list_token_id_,
            source.add_final_list_token() ? marked_source_true_id_
                                          : marked_source_false_id_);
  return vname;
}

bool Verifier::AssertSingleFact(std::string *database, unsigned int fact_id,
                                const kythe::proto::Entry &entry) {
  yy::location loc;
  loc.initialize(database);
  loc.begin.column = 1;
  loc.begin.line = fact_id;
  loc.end = loc.begin;
  Symbol code_symbol = code_id_->AsIdentifier()->symbol();
  AstNode **values = (AstNode **)arena_.New(sizeof(AstNode *) * 5);
  values[0] =
      entry.has_source() ? ConvertVName(loc, entry.source()) : empty_string_id_;
  // We're removing support for ordinal facts. Support them during the
  // transition, but also support the new dot-separated edge kinds that serve
  // the same purpose.
  auto dot_pos = entry.edge_kind().rfind('.');
  bool is_code = false;
  if (dot_pos != std::string::npos && dot_pos > 0 &&
      dot_pos < entry.edge_kind().size() - 1) {
    values[1] = IdentifierFor(loc, entry.edge_kind().substr(0, dot_pos));
    values[3] = ordinal_id_;
    values[4] = IdentifierFor(loc, entry.edge_kind().substr(dot_pos + 1));
  } else {
    values[1] = entry.edge_kind().empty()
                    ? empty_string_id_
                    : IdentifierFor(loc, entry.edge_kind());
    values[3] = entry.fact_name().empty()
                    ? empty_string_id_
                    : IdentifierFor(loc, entry.fact_name());
    if (values[3]->AsIdentifier()->symbol() == code_symbol &&
        convert_marked_source_) {
      // Code facts are turned into subgraphs, so this fact entry will turn
      // into an edge entry.
      if ((values[2] = ConvertCodeFact(loc, entry.fact_value())) == nullptr) {
        return false;
      }
      values[1] = marked_source_code_edge_id_;
      values[3] = root_id_;
      values[4] = empty_string_id_;
      is_code = true;
    } else {
      values[4] = entry.fact_value().empty()
                      ? empty_string_id_
                      : IdentifierFor(loc, entry.fact_value());
    }
  }
  if (!is_code) {
    values[2] = entry.has_target() ? ConvertVName(loc, entry.target())
                                   : empty_string_id_;
  }

  AstNode *tuple = new (&arena_) Tuple(loc, 5, values);
  AstNode *fact = new (&arena_) App(fact_id_, tuple);

  database_prepared_ = false;
  facts_.push_back(fact);
  return true;
}

void Verifier::DumpAsJson() {
  if (!PrepareDatabase()) {
    return;
  }
  // Use the same sort order as we do with Graphviz.
  std::sort(facts_.begin(), facts_.end(), GraphvizSortOrder);
  FileHandlePrettyPrinter printer(stdout);
  QuoteEscapingPrettyPrinter escaping_printer(printer);
  FileHandlePrettyPrinter dprinter(stderr);
  auto DumpAsJson = [this, &printer, &escaping_printer](const char *label,
                                                        AstNode *node) {
    printer.Print(label);
    if (node == empty_string_id()) {
      // Canonicalize "" as null in the JSON output.
      printer.Print("null");
    } else {
      printer.Print("\"");
      node->Dump(symbol_table_, &escaping_printer);
      printer.Print("\"");
    }
  };
  auto DumpVName = [this, &printer, &DumpAsJson](const char *label,
                                                 AstNode *node) {
    printer.Print(label);
    if (node == empty_string_id()) {
      printer.Print("null");
    } else {
      Tuple *vname = node->AsApp()->rhs()->AsTuple();
      printer.Print("{");
      DumpAsJson("\"signature\":", vname->element(0));
      DumpAsJson(",\"corpus\":", vname->element(1));
      DumpAsJson(",\"root\":", vname->element(2));
      DumpAsJson(",\"path\":", vname->element(3));
      DumpAsJson(",\"language\":", vname->element(4));
      printer.Print("}");
    }
  };
  printer.Print("[");
  for (size_t i = 0; i < facts_.size(); ++i) {
    AstNode *fact = facts_[i];
    Tuple *t = fact->AsApp()->rhs()->AsTuple();
    printer.Print("{");
    DumpVName("\"source\":", t->element(0));
    DumpAsJson(",\"edge_kind\":", t->element(1));
    DumpVName(",\"target\":", t->element(2));
    DumpAsJson(",\"fact_name\":", t->element(3));
    DumpAsJson(",\"fact_value\":", t->element(4));
    printer.Print(i + 1 == facts_.size() ? "}" : "},");
  }
  printer.Print("]\n");
}

void Verifier::DumpAsDot() {
  if (!PrepareDatabase()) {
    return;
  }
  std::map<std::string, std::string> vname_labels;
  for (const auto &label_vname : saved_assignments_) {
    if (!label_vname.second) {
      continue;
    }
    if (App *a = label_vname.second->AsApp()) {
      if (Tuple *t = a->rhs()->AsTuple()) {
        StringPrettyPrinter printer;
        QuoteEscapingPrettyPrinter quote_printer(printer);
        label_vname.second->Dump(symbol_table_, &printer);
        auto old_label = vname_labels.find(printer.str());
        if (old_label == vname_labels.end()) {
          vname_labels[printer.str()] = label_vname.first;
        } else {
          old_label->second += ", " + label_vname.first;
        }
      }
    }
  }
  auto GetLabel = [&](AstNode *node) {
    if (!node) {
      return std::string();
    }
    StringPrettyPrinter id_string;
    QuoteEscapingPrettyPrinter id_quote(id_string);
    node->Dump(symbol_table_, &id_string);
    const auto &label = vname_labels.find(id_string.str());
    if (label != vname_labels.end()) {
      return label->second;
    } else {
      return std::string();
    }
  };
  std::sort(facts_.begin(), facts_.end(), GraphvizSortOrder);
  FileHandlePrettyPrinter printer(stdout);
  QuoteEscapingPrettyPrinter quote_printer(printer);
  HtmlEscapingPrettyPrinter html_printer(printer);
  FileHandlePrettyPrinter dprinter(stderr);
  printer.Print("digraph G {\n");
  for (size_t i = 0; i < facts_.size(); ++i) {
    AstNode *fact = facts_[i];
    Tuple *t = fact->AsApp()->rhs()->AsTuple();
    printer.Print("\"");
    t->element(0)->Dump(symbol_table_, &quote_printer);
    printer.Print("\"");
    if (t->element(1) == empty_string_id()) {
      std::string label = GetLabel(t->element(0));
      // Node. We sorted these above st all the facts should come subsequent.
      // Figure out if the node is an anchor.
      bool is_anchor_node = false;
      bool is_file_node = false;
      size_t first_fact = i, last_fact = facts_.size();
      for (; i < facts_.size(); ++i) {
        Tuple *nt = facts_[i]->AsApp()->rhs()->AsTuple();
        if (!EncodedVNameOrIdentEqualTo(nt->element(0), t->element(0)) ||
            nt->element(1) != empty_string_id()) {
          // Moved past the fact block or moved to a different source node.
          last_fact = i;
          break;
        }
        if (EncodedIdentEqualTo(nt->element(3), kind_id_)) {
          if (EncodedIdentEqualTo(nt->element(4), anchor_id_)) {
            // Keep on scanning to find the end of the fact block.
            is_anchor_node = true;
          } else if (EncodedIdentEqualTo(nt->element(4), file_id_)) {
            is_file_node = true;
          }
        }
      }
      if (is_anchor_node) {
        printer.Print(" [ shape=circle, label=\"@");
        printer.Print(label);
        if (!label.empty()) {
          printer.Print("\", color=\"blue");
        }
        printer.Print("\" ];\n");
      } else {
        printer.Print(" [ label=<<TABLE>");
        printer.Print("<TR><TD COLSPAN=\"2\">");
        Tuple *nt = facts_[first_fact]->AsApp()->rhs()->AsTuple();
        // Since all of our facts are well-formed, we know this is a vname.
        nt->element(0)->AsApp()->rhs()->Dump(symbol_table_, &html_printer);
        if (!label.empty()) {
          html_printer.Print(" = ");
          html_printer.Print(label);
        }
        printer.Print("</TD></TR>");
        for (i = first_fact; i < last_fact; ++i) {
          Tuple *nt = facts_[i]->AsApp()->rhs()->AsTuple();
          printer.Print("<TR><TD>");
          nt->element(3)->Dump(symbol_table_, &html_printer);
          printer.Print("</TD><TD>");
          if (is_file_node && EncodedIdentEqualTo(nt->element(3), text_id_)) {
            // Don't clutter the graph with file content.
            printer.Print("...");
          } else if (EncodedIdentEqualTo(nt->element(3), code_id_)) {
            // Don't print encoded proto data.
            printer.Print("...");
          } else {
            nt->element(4)->Dump(symbol_table_, &html_printer);
          }
          printer.Print("</TD></TR>");
        }
        printer.Print("</TABLE>> shape=plaintext ");
        if (!label.empty()) {
          printer.Print(" color=blue ");
        }
        printer.Print("];\n");
      }
      --i;  // Don't skip the fact following the block.
    } else {
      // Edge.
      printer.Print(" -> \"");
      t->element(2)->Dump(symbol_table_, &quote_printer);
      printer.Print("\" [ label=\"");
      t->element(1)->Dump(symbol_table_, &quote_printer);
      if (t->element(4) != empty_string_id()) {
        printer.Print(".");
        t->element(4)->Dump(symbol_table_, &quote_printer);
      }
      printer.Print("\" ];\n");
    }
  }
  printer.Print("}\n");
}

}  // namespace verifier
}  // namespace kythe


text:
require "../progress_bar.rb"
require "test/unit"
require "capture_stdout.rb"

class ProgressBarTest < Test::Unit::TestCase

  [1, 3, 7, 18, 40, 61, 100, 101, 777, 1000].each do |n|
    define_method "test_#{n}_iterations" do
      bar = nil
      scala = capture_stdout { bar = ProgressBar.new(n) }
      expected_nof_points = scala.match(/.*?\[(.*?)\].*/)[1].size
      points = capture_stdout { n.times {bar.next} }
      assert_equal expected_nof_points, points.size
    end
  end

end

redpajama_set_name: RedPajamaGithub


text:
package org.curioswitch.common.server.framework.crypto;

import org.curioswitch.common.server.framework.immutables.JavaBeanStyle;
import org.immutables.value.Value.Immutable;
import org.immutables.value.Value.Modifiable;

@Immutable
@Modifiable
@JavaBeanStyle
public interface SignerConfig {

  /** Base64-encoded private key used to sign payloads. */
  String getPrivateKeyBase64();

  /** Base64-encoded public key used to verify signed payloads. */
  String getPublicKeyBase64();
}


text:
<?php
/**
 * @author @jenschude <jens.schulze@commercetools.de>
 */

namespace Commercetools\Core\Request\Orders;

use Commercetools\Core\Model\Common\Context;
use Commercetools\Core\Request\AbstractByIdGetRequest;
use Commercetools\Core\Request\InStores\InStoreRequestDecorator;
use Commercetools\Core\Request\InStores\InStoreTrait;
use Commercetools\Core\Response\ApiResponseInterface;
use Commercetools\Core\Model\Order\Order;
use Commercetools\Core\Model\MapperInterface;

/**
 * @package Commercetools\Core\Request\Orders
 * @link https://docs.commercetools.com/http-api-projects-orders.html#get-order-by-ordernumber
 * @method Order mapResponse(ApiResponseInterface $response)
 * @method Order mapFromResponse(ApiResponseInterface $response, MapperInterface $mapper = null)
 * @method OrderByOrderNumberGetRequest|InStoreRequestDecorator inStore($storeKey)
 */
class OrderByOrderNumberGetRequest extends AbstractByIdGetRequest
{
    use InStoreTrait;

    protected $resultClass = Order::class;

    /**
     * @param string $orderNumber
     * @param Context $context
     */
    public function __construct($orderNumber, Context $context = null)
    {
        parent::__construct(OrdersEndpoint::endpoint(), $orderNumber, $context);
    }

    /**
     * @param string $orderNumber
     * @param Context $context
     * @return static
     */
    public static function ofOrderNumber($orderNumber, Context $context = null)
    {
        return new static($orderNumber, $context);
    }

    /**
     * @return string
     */
    public function getOrderNumber()
    {
        return $this->getId();
    }

    /**
     * @param string $orderNumber
     * @return $this
     */
    public function setOrderNumber($orderNumber)
    {
        return $this->setId($orderNumber);
    }

    /**
     * @return string
     * @internal
     */
    protected function getPath()
    {
        return (string)$this->getEndpoint() . '/order-number=' . urlencode($this->getId()) . $this->getParamString();
    }
}


text:
import moment from 'moment';
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import TransitionGroup from 'react-transition-group/TransitionGroup';
import FadeTransition from '/imports/ui/components/Transition/Fade';
import FloatButton from '/imports/ui/components/FloatButton';
import DiaryHolder from './DiaryHolder';
import DiaryViewer from './DiaryViewer';

export default class DiaryContent extends PureComponent {
  static propTypes = {
    diarys: PropTypes.array.isRequired,
    history: PropTypes.object.isRequired,
    diaryOpen: PropTypes.func.isRequired,
  }

  _navToWritePage = () => {
    this.props.history.push('/diary/write');
  }

  render() {
    const { diarys } = this.props;
    return [
      <TransitionGroup key="DiaryList">
        {
          diarys.map((diary) => {
            const diaryTime = moment(diary.updatedAt).format('YYYY.MM.DD A HH:mm');
            const diaryWithTime = diary;
            diaryWithTime.time = diaryTime;
            return (
              <FadeTransition key={diary._id} exit={false}>
                <DiaryHolder
                  diary={diaryWithTime}
                  onDiaryClick={(d) => this.props.diaryOpen(d)}
                />
              </FadeTransition>
            );
          })
        }
      </TransitionGroup>,
      <FloatButton key="NavButton" onClick={this._navToWritePage} />,
      <DiaryViewer key="DiaryViewer" />,
    ];
  }
}


text:
package org.unitime.timetable.gwt.client.hql;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import org.unitime.timetable.gwt.client.ToolBox;
import org.unitime.timetable.gwt.client.page.UniTimeNotifications;
import org.unitime.timetable.gwt.client.page.UniTimePageLabel;
import org.unitime.timetable.gwt.client.widgets.LoadingWidget;
import org.unitime.timetable.gwt.client.widgets.SimpleForm;
import org.unitime.timetable.gwt.client.widgets.UniTimeDialogBox;
import org.unitime.timetable.gwt.client.widgets.UniTimeHeaderPanel;
import org.unitime.timetable.gwt.client.widgets.UniTimeTable;
import org.unitime.timetable.gwt.client.widgets.UniTimeTableHeader;
import org.unitime.timetable.gwt.client.widgets.UniTimeTextBox;
import org.unitime.timetable.gwt.client.widgets.UniTimeWidget;
import org.unitime.timetable.gwt.command.client.GwtRpcResponseBoolean;
import org.unitime.timetable.gwt.command.client.GwtRpcResponseList;
import org.unitime.timetable.gwt.command.client.GwtRpcResponseLong;
import org.unitime.timetable.gwt.command.client.GwtRpcResponseNull;
import org.unitime.timetable.gwt.command.client.GwtRpcService;
import org.unitime.timetable.gwt.command.client.GwtRpcServiceAsync;
import org.unitime.timetable.gwt.resources.GwtMessages;
import org.unitime.timetable.gwt.shared.SavedHQLInterface;
import org.unitime.timetable.gwt.shared.EventInterface.EncodeQueryRpcRequest;
import org.unitime.timetable.gwt.shared.EventInterface.EncodeQueryRpcResponse;
import org.unitime.timetable.gwt.shared.SavedHQLInterface.HQLDeleteRpcRequest;
import org.unitime.timetable.gwt.shared.SavedHQLInterface.HQLExecuteRpcRequest;
import org.unitime.timetable.gwt.shared.SavedHQLInterface.HQLOptionsInterface;
import org.unitime.timetable.gwt.shared.SavedHQLInterface.HQLOptionsRpcRequest;
import org.unitime.timetable.gwt.shared.SavedHQLInterface.HQLQueriesRpcRequest;
import org.unitime.timetable.gwt.shared.SavedHQLInterface.HQLSetBackRpcRequest;
import org.unitime.timetable.gwt.shared.SavedHQLInterface.HQLStoreRpcRequest;
import org.unitime.timetable.gwt.shared.SavedHQLInterface.Query;
import org.unitime.timetable.gwt.shared.SavedHQLInterface.Table;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.Widget;

/**
 * @author Tomas Muller
 */
public class SavedHQLPage extends Composite {
	protected static final GwtMessages MESSAGES = GWT.create(GwtMessages.class);
	private static final GwtRpcServiceAsync RPC = GWT.create(GwtRpcService.class);

	private SimpleForm iForm = null;
	private UniTimeHeaderPanel iHeader = null, iTableHeader = null;;
	private UniTimeWidget<ListBox> iQuerySelector = null;
	private HTML iDescription = null;

	private List<SavedHQLInterface.Query> iQueries = new ArrayList<SavedHQLInterface.Query>();
	private List<SavedHQLInterface.Flag> iFlags = new ArrayList<SavedHQLInterface.Flag>();
	private List<SavedHQLInterface.Option> iOptions = new ArrayList<SavedHQLInterface.Option>();
	private UniTimeTable<String[]> iTable = new UniTimeTable<String[]>();
	private String iFirstField = null;
	private String iAppearance = null;
	private int iFirstLine = 0;
	private int iLastSort = 0;
	private String iLastHistory = null;

	public SavedHQLPage() {
		iAppearance = Window.Location.getParameter("appearance");
		if ("courses".equalsIgnoreCase(iAppearance)) {
			UniTimePageLabel.getInstance().setPageName(MESSAGES.pageCourseReports());
		} else if ("exams".equalsIgnoreCase(iAppearance)) {
				UniTimePageLabel.getInstance().setPageName(MESSAGES.pageExaminationReports());
		} else if ("sectioning".equalsIgnoreCase(iAppearance)) {
			UniTimePageLabel.getInstance().setPageName(MESSAGES.pageStudentSectioningReports());
		} else if ("events".equalsIgnoreCase(iAppearance)) {
			UniTimePageLabel.getInstance().setPageName(MESSAGES.pageEventReports());
		} else if ("administration".equalsIgnoreCase(iAppearance)) {
			UniTimePageLabel.getInstance().setPageName(MESSAGES.pageAdministrationReports());
		} else {
			iAppearance = "courses";
			UniTimePageLabel.getInstance().setPageName(MESSAGES.pageCourseReports());
		}

		iForm = new SimpleForm(2);

		iForm.removeStyleName("unitime-NotPrintableBottomLine");

		iHeader = new UniTimeHeaderPanel(MESSAGES.sectFilter());
		iHeader.addButton("execute", MESSAGES.buttonExecute(), 75, new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				iFirstLine = 0;
				iLastSort = 0;
				execute();
			}
		});

		iHeader.addButton("print", MESSAGES.buttonPrint(), 75, new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				HQLExecuteRpcRequest request = new HQLExecuteRpcRequest();
				Long id = Long.valueOf(iQuerySelector.getWidget().getValue(iQuerySelector.getWidget().getSelectedIndex()));
				for (SavedHQLInterface.Query q: iQueries) {
					if (id.equals(q.getId())) {
						request.setQuery(q); break;
					}
				}
				if (request.getQuery() == null) {
					iHeader.setErrorMessage(MESSAGES.errorNoReportSelected());
					return;
				}
				final SimpleForm form = new SimpleForm();
				form.addHeaderRow(request.getQuery().getName());
				if (!request.getQuery().getDescription().isEmpty())
					form.addRow(MESSAGES.propDescription(), new HTML(request.getQuery().getDescription()));

				for (int i = 0; i < iOptions.size(); i++) {
					SavedHQLInterface.Option option = iOptions.get(i);
					if (request.getQuery().getQuery().contains("%" + option.getType() + "%")) {
						ListBox list = ((UniTimeWidget<ListBox>)iForm.getWidget(3 + i, 1)).getWidget();
						String value = "";
						String values = "";
						boolean allSelected = true;
						if (list.isMultipleSelect()) {
							for (int j = 0; j < list.getItemCount(); j++)
								if (list.isItemSelected(j)) {
									if (!value.isEmpty()) { value += ","; values += ", "; }
									value += list.getValue(j);
									values += list.getItemText(j);
								} else {
									allSelected = false;
								}
							if (allSelected) values = MESSAGES.itemAll();
						} else if (list.getSelectedIndex() > 0) {
							value = list.getValue(list.getSelectedIndex());
							values = list.getItemText(list.getSelectedIndex());
						}
						if (value.isEmpty()) {
							iHeader.setErrorMessage(MESSAGES.errorItemNotSelected(option.getName()));
							return;
						}
						request.addOption(option.getType(), value);
						form.addRow(option.getName() + ":", new Label(values, true));
					}
				}
				LoadingWidget.getInstance().show(MESSAGES.waitExecuting(request.getQuery().getName()));
				request.setFromRow(0); request.setMaxRows(10000);
				RPC.execute(request, new AsyncCallback<Table>() {
					@Override
					public void onFailure(Throwable caught) {
						iTableHeader.setErrorMessage(caught.getMessage(), true);
						LoadingWidget.getInstance().hide();
					}

					@Override
					public void onSuccess(Table result) {
						LoadingWidget.getInstance().hide();
						if (result == null || result.size() <= 1) {
							iTableHeader.setMessage(MESSAGES.errorNoResults());
							return;
						}
						final UniTimeTable<String[]> table = new UniTimeTable<String[]>();
						String firstField = null;
						int nrCols = 0;
						for (int i = 0; i < result.size(); i++) {
							String[] row = result.get(i);
							List<Widget> line = new ArrayList<Widget>();
							if (i == 0) {
								firstField = row[0]; nrCols = row.length;
								for (String x: row) {
									final String name = x.replace('_', ' ').trim();
									UniTimeTableHeader h = new UniTimeTableHeader(name, 1);
									line.add(h);
								}
							} else {
								for (String x: row) {
									line.add(new HTML(x == null ? "" : x.replace("\\n", "<br>")));
								}
							}
							table.addRow(i == 0 ? null : row, line);
						}
						if (firstField != null && firstField.startsWith("__"))
							table.setColumnVisible(0, false);
						if (iLastSort != 0 && Math.abs(iLastSort) <= nrCols) {
							table.sort(table.getHeader(Math.abs(iLastSort) - 1), new Comparator<String[]>() {
								@Override
								public int compare(String[] o1, String[] o2) {
									return SavedHQLPage.compare(o1, o2, Math.abs(iLastSort) - 1);
								}
							}, iLastSort > 0);
						}
						table.getElement().getStyle().setWidth(1040, Unit.PX);

						// Move header row to thead
						Element headerRow = table.getRowFormatter().getElement(0);
						Element tableElement = table.getElement();
						Element thead = DOM.createTHead();
						tableElement.insertFirst(thead);
						headerRow.getParentElement().removeChild(headerRow);
						thead.appendChild(headerRow);

						final Element div = DOM.createDiv();
						div.appendChild(form.getElement());
						div.appendChild(table.getElement());

						String name = MESSAGES.pageCourseReports();
						if ("courses".equalsIgnoreCase(iAppearance)) {
							name = MESSAGES.pageCourseReports();
						} else if ("exams".equalsIgnoreCase(iAppearance)) {
							name = MESSAGES.pageExaminationReports();
						} else if ("sectioning".equalsIgnoreCase(iAppearance)) {
							name = MESSAGES.pageStudentSectioningReports();
						} else if ("events".equalsIgnoreCase(iAppearance)) {
							name = MESSAGES.pageEventReports();
						} else if ("administration".equalsIgnoreCase(iAppearance)) {
							name = MESSAGES.pageAdministrationReports();
						}
						final String pageName = name;

						ToolBox.print(new ToolBox.Page() {
							@Override
							public String getName() { return pageName; }
							@Override
							public String getUser() { return ""; }
							@Override
							public String getSession() { return ""; }
							@Override
							public Element getBody() { return div; }
						});

					}
				});
			}
		});

		iHeader.addButton("export", MESSAGES.buttonExportCSV(), 85, new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				Long id = Long.valueOf(iQuerySelector.getWidget().getValue(iQuerySelector.getWidget().getSelectedIndex()));
				SavedHQLInterface.Query query = null;
				for (SavedHQLInterface.Query q: iQueries) {
					if (id.equals(q.getId())) {
						query = q; break;
					}
				}
				if (query == null) {
					iHeader.setErrorMessage(MESSAGES.errorNoReportSelected());
					return;
				}
				String params = "";
				for (int i = 0; i < iOptions.size(); i++) {
					SavedHQLInterface.Option option = iOptions.get(i);
					if (query.getQuery().contains("%" + option.getType() + "%")) {
						ListBox list = ((UniTimeWidget<ListBox>)iForm.getWidget(3 + i, 1)).getWidget();
						String value = "";
						boolean allSelected = true;
						if (list.isMultipleSelect()) {
							for (int j = 0; j < list.getItemCount(); j++)
								if (list.isItemSelected(j)) {
									if (!value.isEmpty()) value += ",";
									value += list.getValue(j);
								} else {
									allSelected = false;
								}
						} else if (list.getSelectedIndex() > 0) {
							value = list.getValue(list.getSelectedIndex());
						}
						if (value.isEmpty()) {
							iHeader.setErrorMessage(MESSAGES.errorItemNotSelected(option.getName()));
							return;
						}
						if (!params.isEmpty()) params += ":";
						params += (list.isMultipleSelect() && allSelected ? "" : value);
					}
				}
				String reportId = iQuerySelector.getWidget().getValue(iQuerySelector.getWidget().getSelectedIndex());

				RPC.execute(EncodeQueryRpcRequest.encode("output=hql-report.csv&report=" + reportId + "&params=" + params + "&sort=" + iLastSort), new AsyncCallback<EncodeQueryRpcResponse>() {
					@Override
					public void onFailure(Throwable caught) {
					}
					@Override
					public void onSuccess(EncodeQueryRpcResponse result) {
						ToolBox.open(GWT.getHostPageBaseURL() + "export?q=" + result.getQuery());
					}
				});
			}
		});

		iHeader.addButton("edit", MESSAGES.buttonEdit(), 75, new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				SavedHQLInterface.Query query = null;
				Long id = Long.valueOf(iQuerySelector.getWidget().getValue(iQuerySelector.getWidget().getSelectedIndex()));
				for (SavedHQLInterface.Query q: iQueries) {
					if (id.equals(q.getId())) {
						query = q; break;
					}
				}
				if (query == null) {
					iHeader.setErrorMessage(MESSAGES.errorNoReportSelected());
					return;
				}
				openDialog(query);
			}
		});

		iHeader.addButton("add", MESSAGES.buttonAddNew(), 75, new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				openDialog(null);
			}
		});

		iForm.addHeaderRow(iHeader);
		iHeader.setEnabled("execute", false);
		iHeader.setEnabled("edit", false);
		iHeader.setEnabled("add", false);
		iHeader.setEnabled("print", false);
		iHeader.setEnabled("export", false);

		iForm.getColumnFormatter().setWidth(0, "120px");
		iForm.getColumnFormatter().setWidth(1, "100%");

		iQuerySelector = new UniTimeWidget<ListBox>(new ListBox());
		iForm.addRow(MESSAGES.propReport(), iQuerySelector);
		iQuerySelector.getWidget().addChangeHandler(new ChangeHandler() {
			@Override
			public void onChange(ChangeEvent event) {
				iQuerySelector.clearHint();
				iQuerySelector.setPrintText(iQuerySelector.getWidget().getItemText(iQuerySelector.getWidget().getSelectedIndex()));
				queryChanged();
			}
		});
		iDescription = new HTML("");
		iForm.addRow(MESSAGES.propDescription(), iDescription);
		iForm.getCellFormatter().setVerticalAlignment(2, 0, HasVerticalAlignment.ALIGN_TOP);

		LoadingWidget.getInstance().show(MESSAGES.waitLoadingReports());
		RPC.execute(new HQLOptionsRpcRequest(), new AsyncCallback<HQLOptionsInterface>() {
			@Override
			public void onFailure(Throwable caught) {
				iHeader.setErrorMessage(caught.getMessage());
				LoadingWidget.getInstance().hide();
				ToolBox.checkAccess(caught);
			}

			@Override
			public void onSuccess(HQLOptionsInterface result) {
				iFlags = result.getFlags();
				iOptions = result.getOptions();
				iHeader.setEnabled("add", result.isEditable());
				for (int i = 0; i < iOptions.size(); i++) {
					SavedHQLInterface.Option option = iOptions.get(i);
					ListBox list = new ListBox();
					list.setMultipleSelect(option.isMultiSelect());
					if (!option.isMultiSelect())
						list.addItem(MESSAGES.itemSelect(), "-1");
					for (SavedHQLInterface.IdValue v: option.values())
						list.addItem(v.getText(), v.getValue());
					final UniTimeWidget<ListBox> u = new UniTimeWidget<ListBox>(list);
					iForm.addRow(option.getName() + ":", u);
					iForm.getCellFormatter().setVerticalAlignment(3 + i, 0, HasVerticalAlignment.ALIGN_TOP);
					iForm.getRowFormatter().setVisible(3 + i, false);
					if (list.isMultipleSelect()) {
						for (int j = 0; j < list.getItemCount(); j++)
							list.setItemSelected(j, true);
						u.setPrintText(MESSAGES.itemAll());
					} else if (list.getItemCount() == 2) {
						list.setSelectedIndex(1);
						u.setPrintText(list.getItemText(1));
					}
					list.addChangeHandler(new ChangeHandler() {
						@Override
						public void onChange(ChangeEvent event) {
							u.clearHint();
							String selected = "";
							boolean hasAll = true;
							for (int i = 0; i < u.getWidget().getItemCount(); i++) {
								if (u.getWidget().isItemSelected(i)) {
									if (!selected.isEmpty()) selected += ",";
									selected += u.getWidget().getItemText(i);
								} else hasAll = false;
							}
							if (hasAll && u.getWidget().getItemCount() > 5)
								selected = MESSAGES.itemAll();
							if (selected.length() > 150)
								selected = selected.substring(0, 147) + "...";
							u.setPrintText(selected);
							iHeader.clearMessage();
						}
					});
				}
				iTableHeader = new UniTimeHeaderPanel(MESSAGES.sectResults());
				iTableHeader.addButton("previous", MESSAGES.buttonPrevious(), 75, new ClickHandler() {
					@Override
					public void onClick(ClickEvent event) {
						iFirstLine -= 100;
						execute();
					}
				});
				iTableHeader.addButton("next", MESSAGES.buttonNext(), 75, new ClickHandler() {
					@Override
					public void onClick(ClickEvent event) {
						iFirstLine += 100;
						execute();
					}
				});
				iTableHeader.setEnabled("previous", false);
				iTableHeader.setEnabled("next", false);
				iForm.addHeaderRow(iTableHeader);
				iForm.addRow(iTable);
				iForm.addBottomRow(iHeader.clonePanel(""));
				loadQueries(null, true);
			}
		});

		iTable.addMouseClickListener(new UniTimeTable.MouseClickListener<String[]>() {
			@Override
			public void onMouseClick(UniTimeTable.TableEvent<String[]> event) {
				if (event.getRow() > 0 && event.getData() != null) {
					if ("__Class".equals(iFirstField))
						ToolBox.open(GWT.getHostPageBaseURL() + "classDetail.do?cid=" + event.getData()[0]);
					else if ("__Offering".equals(iFirstField))
						ToolBox.open(GWT.getHostPageBaseURL() + "instructionalOfferingDetail.do?op=view&io=" + event.getData()[0]);
					else if ("__Subpart".equals(iFirstField))
						ToolBox.open(GWT.getHostPageBaseURL() + "schedulingSubpartDetail.do?ssuid=" + event.getData()[0]);
					else if ("__Room".equals(iFirstField))
						ToolBox.open(GWT.getHostPageBaseURL() + "gwt.jsp?page=rooms&back=1&id=" + event.getData()[0]);
					else if ("__Instructor".equals(iFirstField))
						ToolBox.open(GWT.getHostPageBaseURL() + "instructorDetail.do?instructorId=" + event.getData()[0]);
					else if ("__Exam".equals(iFirstField))
						ToolBox.open(GWT.getHostPageBaseURL() + "examDetail.do?examId=" + event.getData()[0]);
					else if ("__Event".equals(iFirstField))
						ToolBox.open(GWT.getHostPageBaseURL() + "gwt.jsp?page=events#event=" + event.getData()[0]);
				}
			}
		});

		initWidget(iForm);

		History.addValueChangeHandler(new ValueChangeHandler<String>() {
			@Override
			public void onValueChange(ValueChangeEvent<String> event) {
				reload(event.getValue());
			}
		});
	}

	UniTimeDialogBox iDialog = null;
	SimpleForm iDialogForm = null;
	UniTimeHeaderPanel iDialogHeader = null;
	SavedHQLInterface.Query iDialogQuery = null;
	UniTimeTextBox iDialogName = null;
	TextArea iDialogDescription = null;
	TextArea iDialogQueryArea = null;
	ListBox iDialogAppearance = null;

	public void openDialog(SavedHQLInterface.Query q) {
		iDialogQuery = q;
		if (iDialog == null) {
			iDialog = new UniTimeDialogBox(true, false);
			iDialogForm = new SimpleForm();
			iDialogName = new UniTimeTextBox(100, 680);
			iDialogForm.addRow(MESSAGES.propName(), iDialogName);
			iDialogDescription = new TextArea();
			iDialogDescription.setStyleName("unitime-TextArea");
			iDialogDescription.setVisibleLines(5);
			iDialogDescription.setCharacterWidth(120);
			iDialogForm.addRow(MESSAGES.propDescription(), iDialogDescription);
			iDialogForm.getCellFormatter().setVerticalAlignment(1, 0, HasVerticalAlignment.ALIGN_TOP);
			iDialogQueryArea = new TextArea();
			iDialogQueryArea.setStyleName("unitime-TextArea");
			iDialogQueryArea.setVisibleLines(10);
			iDialogQueryArea.setCharacterWidth(120);
			iDialogForm.addRow(MESSAGES.propQuery(), iDialogQueryArea);
			iDialogForm.getCellFormatter().setVerticalAlignment(2, 0, HasVerticalAlignment.ALIGN_TOP);
			for (int i = 0; i < iFlags.size(); i++) {
				SavedHQLInterface.Flag f = iFlags.get(i);
				CheckBox ch = new CheckBox(f.getText());
				iDialogForm.addRow(i == 0 ? MESSAGES.propFlags() : "", ch);
			}
			iDialogHeader = new UniTimeHeaderPanel();
			iDialogForm.addBottomRow(iDialogHeader);
			iDialogHeader.addButton("save", MESSAGES.opQuerySave(), 75, new ClickHandler() {
				@Override
				public void onClick(ClickEvent event) {
					iDialogQuery.setName(iDialogName.getText());
					if (iDialogName.getText().isEmpty()) {
						iDialogHeader.setErrorMessage(MESSAGES.errorNameIsRequired());
						return;
					}
					iDialogQuery.setDescription(iDialogDescription.getText());
					iDialogQuery.setQuery(iDialogQueryArea.getText());
					if (iDialogQueryArea.getText().isEmpty()) {
						iDialogHeader.setErrorMessage(MESSAGES.errorQueryIsRequired());
						return;
					}
					int flags = 0;
					boolean hasAppearance = false;
					for (int i = 0; i < iFlags.size(); i++) {
						SavedHQLInterface.Flag f = iFlags.get(i);
						CheckBox ch = (CheckBox)iDialogForm.getWidget(3 + i, 1);
						if (ch.getValue()) {
							flags += f.getValue();
							if (f.isAppearance()) hasAppearance = true;
						}
					}
					if (!hasAppearance) {
						iDialogHeader.setErrorMessage(MESSAGES.errorNoAppearanceSelected());
						return;
					}
					iDialogQuery.setFlags(flags);
					RPC.execute(new HQLStoreRpcRequest(iDialogQuery), new AsyncCallback<GwtRpcResponseLong>() {
						@Override
						public void onFailure(Throwable caught) {
							iDialogHeader.setErrorMessage(caught.getMessage());
						}

						@Override
						public void onSuccess(GwtRpcResponseLong result) {
							iDialog.hide();
							loadQueries(result.getValue(), false);
						}
					});
				}
			});
			iDialogHeader.addButton("test", MESSAGES.opQueryTest(), 75, new ClickHandler() {
				@Override
				public void onClick(ClickEvent event) {
					if (iDialogQueryArea.getText().isEmpty()) {
						iDialogHeader.setErrorMessage(MESSAGES.errorQueryIsRequired());
						return;
					}
					iDialogQuery.setQuery(iDialogQueryArea.getText());
					LoadingWidget.getInstance().show(MESSAGES.waitTestingQuery());
					HQLExecuteRpcRequest request = new HQLExecuteRpcRequest();
					request.setQuery(iDialogQuery);
					request.setFromRow(0);
					request.setMaxRows(101);
					RPC.execute(request, new AsyncCallback<Table>() {
						@Override
						public void onFailure(Throwable caught) {
							iDialogHeader.setErrorMessage(MESSAGES.failedTestNoReason());
							LoadingWidget.getInstance().hide();
							UniTimeNotifications.error(MESSAGES.failedTest(caught.getMessage()), caught);
						}

						@Override
						public void onSuccess(Table result) {
							iDialogHeader.setMessage(result.size() <= 1 ? MESSAGES.infoTestSucceededNoResults() : result.size() > 101 ? MESSAGES.infoTestSucceededWith100OrMoreRows() : MESSAGES.infoTestSucceededWithRows(result.size() - 1));
							LoadingWidget.getInstance().hide();
						}
					});
				}
			});
			iDialogHeader.addButton("export", MESSAGES.opScriptExport(), new ClickHandler() {
				@Override
				public void onClick(ClickEvent event) {
					RPC.execute(EncodeQueryRpcRequest.encode("output=hql.xml&id=" + iDialogQuery.getId()), new AsyncCallback<EncodeQueryRpcResponse>() {
						@Override
						public void onFailure(Throwable caught) {
						}
						@Override
						public void onSuccess(EncodeQueryRpcResponse result) {
							ToolBox.open(GWT.getHostPageBaseURL() + "export?q=" + result.getQuery());
						}
					});
				}
			});
			iDialogHeader.addButton("delete", MESSAGES.opQueryDelete(), 75, new ClickHandler() {
				@Override
				public void onClick(ClickEvent event) {
					RPC.execute(new HQLDeleteRpcRequest(iDialogQuery.getId()), new AsyncCallback<GwtRpcResponseBoolean>() {
						@Override
						public void onFailure(Throwable caught) {
							iDialogHeader.setErrorMessage(caught.getMessage());
						}

						@Override
						public void onSuccess(GwtRpcResponseBoolean result) {
							iDialog.hide();
							loadQueries(null, false);
						}
					});
				}
			});
			iDialogHeader.addButton("back", MESSAGES.opQueryBack(), 75, new ClickHandler() {
				@Override
				public void onClick(ClickEvent event) {
					iDialog.hide();
				}
			});
			iDialog.setWidget(iDialogForm);
		}
		iDialog.setText(q == null ? MESSAGES.dialogNewReport() : MESSAGES.dialogEditReport(q.getName()));
		iDialogHeader.setEnabled("export", q != null);
		iDialogHeader.setEnabled("delete", q != null);
		iDialogHeader.clearMessage();
		iDialogName.setText(q == null ? "" : q.getName());
		iDialogDescription.setText(q == null ? "" : q.getDescription());
		iDialogQueryArea.setText(q == null ? "" : q.getQuery());
		for (int i = 0; i < iFlags.size(); i++) {
			SavedHQLInterface.Flag f = iFlags.get(i);
			CheckBox ch = (CheckBox)iDialogForm.getWidget(3 + i, 1);
			ch.setValue(q == null ? iAppearance.equals(f.getAppearance()) : (q.getFlags() & f.getValue()) != 0);
		}
		if (iDialogQuery == null) iDialogQuery = new SavedHQLInterface.Query();
		iDialog.center();
	}

	public void loadQueries(final Long select, final boolean reload) {
		if (!LoadingWidget.getInstance().isShowing())
			LoadingWidget.getInstance().show(MESSAGES.waitLoadingReports());
		RPC.execute(new HQLQueriesRpcRequest(iAppearance), new AsyncCallback<GwtRpcResponseList<Query>>() {
			@Override
			public void onFailure(Throwable caught) {
				iHeader.setErrorMessage(caught.getMessage());
				LoadingWidget.getInstance().hide();
			}
			@Override
			public void onSuccess(GwtRpcResponseList<Query> result) {
				String selected = (select == null ? null : select.toString());
				if (selected == null && iQuerySelector.getWidget().getSelectedIndex() >= 0) {
					selected = iQuerySelector.getWidget().getValue(iQuerySelector.getWidget().getSelectedIndex());
				}
				iQuerySelector.getWidget().clear();
				if (result.isEmpty()) {
					iHeader.setErrorMessage(MESSAGES.errorNoReportsAvailable());
				} else {
					iQuerySelector.getWidget().addItem(MESSAGES.itemSelect(), "-1");
					iQueries = result;
					for (int i = 0; i < result.size(); i++) {
						iQuerySelector.getWidget().addItem(result.get(i).getName(), result.get(i).getId().toString());
						if (selected != null && selected.equals(result.get(i).getId().toString()))
							iQuerySelector.getWidget().setSelectedIndex(1 + i);
					}
					queryChanged();
				}
				LoadingWidget.getInstance().hide();
				if (reload) reload(History.getToken());
			}
		});
	}

	private void queryChanged() {
		iHeader.clearMessage();
		if (iQuerySelector.getWidget().getSelectedIndex() <= 0) {
			iHeader.setEnabled("execute", false);
			iHeader.setEnabled("edit", false);
			iDescription.setHTML("");
			for (int i = 0; i < iOptions.size(); i++)
				iForm.getRowFormatter().setVisible(3 + i, false);
		} else {
			iHeader.setEnabled("execute", true);
			Long id = Long.valueOf(iQuerySelector.getWidget().getValue(iQuerySelector.getWidget().getSelectedIndex()));
			for (SavedHQLInterface.Query q: iQueries) {
				if (id.equals(q.getId())) {
					iDescription.setHTML(q.getDescription());
					iHeader.setEnabled("edit", iHeader.isEnabled("add"));
					for (int i = 0; i < iOptions.size(); i++) {
						SavedHQLInterface.Option option = iOptions.get(i);
						iForm.getRowFormatter().setVisible(3 + i, q.getQuery().contains("%" + option.getType() + "%"));
					}
				}
			}
		}
	}

	public static int compare(String[] a, String[] b, int col) {
		for (int i = 0; i < a.length; i++) {
			int c = (col + i) % a.length;
			try {
				int cmp = Double.valueOf(a[c] == null ? "0" : a[c]).compareTo(Double.valueOf(b[c] == null ? "0" : b[c]));
				if (cmp != 0) return cmp;
			} catch (NumberFormatException e) {
				int cmp = (a[c] == null ? "" : a[c]).compareTo(b[c] == null ? "" : b[c]);
				if (cmp != 0) return cmp;
			}
		}
		return 0;
	}

	public void populate(Table result) {
		if (result == null || result.size() <= 1) {
			iTableHeader.setMessage(MESSAGES.errorNoResults());
		} else {
			for (int i = 0; i < result.size(); i++) {
				String[] row = result.get(i);
				List<Widget> line = new ArrayList<Widget>();
				if (i == 0) {
					iFirstField = row[0];
					for (String x: row) {
						final String name = x.replace('_', ' ').trim();
						final UniTimeTableHeader h = new UniTimeTableHeader(name, 1);
						final int col = line.size();
						h.addOperation(new UniTimeTableHeader.Operation() {
							@Override
							public void execute() {
								iTable.sort(col, new Comparator<String[]>() {
									@Override
									public int compare(String[] o1, String[] o2) {
										return SavedHQLPage.compare(o1, o2, col);
									}
								});
								iLastSort = (h.getOrder() != null && h.getOrder() ? (1 + col) : -1 - col);
								History.newItem(iLastHistory + ":" + iFirstLine + ":" + iLastSort, false);
								setBack();
							}
							@Override
							public boolean isApplicable() {
								return true;
							}
							@Override
							public boolean hasSeparator() {
								return false;
							}
							@Override
							public String getName() {
								return MESSAGES.opSortBy(name);
							}
						});
						line.add(h);
					}
				} else {
					for (String x: row) {
						line.add(new HTML(x == null ? "" : x.replace("\\n", "<br>")));
					}
				}
				iTable.addRow(i == 0 ? null : row, line);
			}
			if (iFirstField != null && iFirstField.startsWith("__"))
				iTable.setColumnVisible(0, false);
			iHeader.setEnabled("print", true);
			iHeader.setEnabled("export", iTable.getRowCount() > 1);
			if (result.size() == 102)
				iTableHeader.setMessage(MESSAGES.infoShowingLines(iFirstLine + 1, iFirstLine + 100));
			else if (iFirstLine > 0)
				iTableHeader.setMessage(MESSAGES.infoShowingLines(iFirstLine + 1, iFirstLine + result.size() - 1));
			else
				iTableHeader.setMessage(MESSAGES.infoShowingAllLines(result.size() - 1));
			iTableHeader.setEnabled("next", result.size() > 101);
			iTableHeader.setEnabled("previous", iFirstLine > 0);
			if (iLastSort != 0) {
				iTable.sort(iTable.getHeader(Math.abs(iLastSort) - 1), new Comparator<String[]>() {
					@Override
					public int compare(String[] o1, String[] o2) {
						return SavedHQLPage.compare(o1, o2, Math.abs(iLastSort) - 1);
					}
				}, iLastSort > 0);
			}
		}
		setBack();
	}

	public void setBack() {
		if (iFirstField == null || !iFirstField.startsWith("__") || iTable.getRowCount() <= 1) return;
		HQLSetBackRpcRequest request = new HQLSetBackRpcRequest();
		for (int i = 1; i < iTable.getRowCount(); i++) {
			String[] row = iTable.getData(i);
			if (row != null) {
				Long id = Long.valueOf(row[0]);
				request.addId(id);
			}
		}
		request.setAppearance(iAppearance);
		request.setHistory(History.getToken());
		request.setType(iFirstField);
		RPC.execute(request, new AsyncCallback<GwtRpcResponseNull>() {
			@Override
			public void onFailure(Throwable caught) {
				iHeader.setErrorMessage(caught.getMessage());
			}

			@Override
			public void onSuccess(GwtRpcResponseNull result) {
			}
		});
	}

	public void reload(String history) {
		if (history == null) return;
		if (history.indexOf('&') >= 0)
			history = history.substring(0, history.indexOf('&'));
		if (history.isEmpty()) return;
		String[] params = history.split(":");
		Long id = Long.valueOf(params[0]);
		SavedHQLInterface.Query query = null;
		for (int i = 0; i < iQueries.size(); i++) {
			SavedHQLInterface.Query q = iQueries.get(i);
			if (id.equals(q.getId())) {
				query = q;
				iQuerySelector.getWidget().setSelectedIndex(1 + i);
				queryChanged();
				break;
			}
		}
		if (query == null) return;
		int idx = 1;
		for (int i = 0; i < iOptions.size(); i++) {
			SavedHQLInterface.Option option = iOptions.get(i);
			if (query.getQuery().contains("%" + option.getType() + "%")) {
				String param = params[idx++];
				if (param == null || param.isEmpty()) continue;
				ListBox list = ((UniTimeWidget<ListBox>)iForm.getWidget(3 + i, 1)).getWidget();
				if (list.isMultipleSelect()) {
					for (int j = 0; j < list.getItemCount(); j++) {
						String value = list.getValue(j);
						boolean contains = false;
						for (String o: param.split(",")) if (o.equals(value)) { contains = true; break; }
						list.setItemSelected(j, contains);
					}
				} else {
					for (int j = 1; j < list.getItemCount(); j++) {
						if (list.getValue(j).equals(param)) {
							list.setSelectedIndex(j); break;
						}
					}
				}
			}
		}
		iFirstLine = Integer.parseInt(params[idx++]);
		iLastSort = Integer.parseInt(params[idx++]);
		execute();
	}

	private void execute() {
		HQLExecuteRpcRequest request = new HQLExecuteRpcRequest();
		iHeader.setEnabled("print", false);
		iHeader.setEnabled("export", false);

		Long id = Long.valueOf(iQuerySelector.getWidget().getValue(iQuerySelector.getWidget().getSelectedIndex()));
		for (SavedHQLInterface.Query q: iQueries) {
			if (id.equals(q.getId())) {
				request.setQuery(q); break;
			}
		}
		if (request.getQuery() == null) {
			iHeader.setErrorMessage(MESSAGES.errorNoReportSelected());
			return;
		}
		iLastHistory = request.getQuery().getId().toString();
		for (int i = 0; i < iOptions.size(); i++) {
			SavedHQLInterface.Option option = iOptions.get(i);
			if (request.getQuery().getQuery().contains("%" + option.getType() + "%")) {
				SavedHQLInterface.IdValue o = new SavedHQLInterface.IdValue();
				o.setValue(option.getType());
				ListBox list = ((UniTimeWidget<ListBox>)iForm.getWidget(3 + i, 1)).getWidget();
				String value = "";
				boolean allSelected = true;
				if (list.isMultipleSelect()) {
					for (int j = 0; j < list.getItemCount(); j++)
						if (list.isItemSelected(j)) {
							if (!value.isEmpty()) value += ",";
							value += list.getValue(j);
						} else {
							allSelected = false;
						}
				} else if (list.getSelectedIndex() > 0) {
					value = list.getValue(list.getSelectedIndex());
				}
				if (value.isEmpty()) {
					iHeader.setErrorMessage(MESSAGES.errorItemNotSelected(option.getName()));
					return;
				}
				request.addOption(option.getType(), value);
				iLastHistory += ":" + (list.isMultipleSelect() && allSelected ? "" : value);
			}
		}

		iTable.clearTable(); iFirstField = null;
		iTableHeader.clearMessage();
		iHeader.clearMessage();
		LoadingWidget.getInstance().show(MESSAGES.waitExecuting(request.getQuery().getName()));
		History.newItem(iLastHistory + ":" + iFirstLine + ":" + iLastSort, false);
		request.setFromRow(iFirstLine);
		request.setMaxRows(101);
		RPC.execute(request, new AsyncCallback<Table>() {
			@Override
			public void onFailure(Throwable caught) {
				iTableHeader.setErrorMessage(caught.getMessage(), true);
				LoadingWidget.getInstance().hide();
			}

			@Override
			public void onSuccess(Table result) {
				populate(result);
				LoadingWidget.getInstance().hide();
			}
		});
	}

}


text: The new repository of DO-MPC is [here] (https://github.com/do-mpc/do-mpc/wiki)


text:
package org.onlab.netty;

import static com.google.common.base.Preconditions.*;

import java.util.Objects;

import org.onlab.packet.IpAddress;

import com.google.common.base.MoreObjects;

/**
 * Representation of a TCP/UDP communication end point.
 */
public final class Endpoint {

    private final int port;
    private final IpAddress ip;

    public Endpoint(IpAddress host, int port) {
        this.ip = checkNotNull(host);
        this.port = port;
    }

    public IpAddress host() {
        return ip;
    }

    public int port() {
        return port;
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(getClass())
                .add("ip", ip)
                .add("port", port)
                .toString();
    }

    @Override
    public int hashCode() {
        return Objects.hash(ip, port);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        Endpoint that = (Endpoint) obj;
        return Objects.equals(this.port, that.port) &&
               Objects.equals(this.ip, that.ip);
    }
}


text:
<!DOCTYPE html>

    <title>My Word Clock</title>
    <link rel="stylesheet" type="text/css" href="wordClock.css">