René Nyffenegger's collection of things on the web
René Nyffenegger on Oracle - Most wanted - Feedback -
 

class Group [JoS Reader]

Each group that can be read with the Joel on Software reader is represented by an instance of this class. Currently, these groups are: Joel on Software, Business of Software, Design of Software and Off topic.
If an instance of a Group is needed, but has not yet been created, it is created in findgroup().

The header file (Group.h)

app/Group.h
/*
   JOSReader.h

   Copyright (C) 2004-2005 René Nyffenegger

   This source code is provided 'as-is', without any express or implied
   warranty. In no event will the author be held liable for any damages
   arising from the use of this software.

   Permission is granted to anyone to use this software for any purpose,
   including commercial applications, and to alter it and redistribute it
   freely, subject to the following restrictions:

   1. The origin of this source code must not be misrepresented; you must not
      claim that you wrote the original source code. If you use this source code
      in a product, an acknowledgment in the product documentation would be
      appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and must not be
      misrepresented as being the original source code.

   3. This notice may not be removed or altered from any source distribution.


   Group.h is part of "Joel on Software reader".
   The most current version of Joel on Software reader can be found at 
   http://www.adp-gmbh.ch/misc/jos_reader/index.html

   René Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/

#ifndef GROUP_H__
#define GROUP_H__

#include <vector>
#include <string>

#include "Topic.h"

class PCREWrapper;
class SQLiteStatement;

class Group {
  public:
    Group(std::string const& short_group_name, std::string const& title);

    Group();

    void Refresh();

    void   RewindTopic();
    Topic* NextTopic  ();
    Topic* TopicWithId(std::string const& id);

    Topic  CreateOrRetrieveTopic(
        std::string group_name,
        std::string group_id,
        std::string topic_id,
        std::string replies,
        std::string title
        );

    int    LastTopicIdSeen();

    std::string                  title_;

  private:
    std::string                  short_group_name_;

    void ReadFromServer();

    void Init();

    std::vector<Topic>           topics_;

    std::vector<Topic>::iterator cur_topic_;

    static PCREWrapper*          re_topic_author;
    static PCREWrapper*          re_topic       ;

    static SQLiteStatement*      sql_select_last_topic_id;
    static SQLiteStatement*      sql_update_last_topic_id;
    static SQLiteStatement*      sql_select_topic;
    static SQLiteStatement*      sql_insert_topic;

    int    last_topic_id_seen_;
};

#endif

The implementation file (Group.cpp)

app/Group.cpp
/*
   Group.cpp

   Copyright (C) 2004-2005 René Nyffenegger

   This source code is provided 'as-is', without any express or implied
   warranty. In no event will the author be held liable for any damages
   arising from the use of this software.

   Permission is granted to anyone to use this software for any purpose,
   including commercial applications, and to alter it and redistribute it
   freely, subject to the following restrictions:

   1. The origin of this source code must not be misrepresented; you must not
      claim that you wrote the original source code. If you use this source code
      in a product, an acknowledgment in the product documentation would be
      appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and must not be
      misrepresented as being the original source code.

   3. This notice may not be removed or altered from any source distribution.

   Group.cpp is part of "Joel on Software reader".
   The most current version of Joel on Software reader can be found at 
   http://www.adp-gmbh.ch/misc/jos_reader/index.html

   René Nyffenegger rene.nyffenegger@adp-gmbh.ch

*/
#include "Group.h"
#include "JOSReader.h"

#include "PCREWrapper.h"
#include "Socket.h"
#include "stdhelpers.h"

// TODO: take this include out
#include <windows.h>

// TODO: delete re_topic
// TODO: delete re_topic_author
PCREWrapper* Group::re_topic        = 0;
PCREWrapper* Group::re_topic_author = 0;

SQLiteStatement* Group::sql_select_last_topic_id = 0;
SQLiteStatement* Group::sql_update_last_topic_id = 0;
SQLiteStatement* Group::sql_select_topic         = 0;
SQLiteStatement* Group::sql_insert_topic         = 0;

Group::Group(std::string const& short_group_name, std::string const& title) 
  :  title_(title), short_group_name_(short_group_name)
{
  Init();
  ReadFromServer();
}

Group::Group() {
  Init();
}

int Group::LastTopicIdSeen() {
  return last_topic_id_seen_;
}

void Group::Init() {
  if (!re_topic)        re_topic        = new PCREWrapper("<a class=\"discuss\" href=\"default.asp\\?([^.]+)\\.(\\d+)\\.(\\d+)\\.(\\d+)\">(.*)</a>", "re_topic");
  if (!re_topic_author) re_topic_author = new PCREWrapper("<em>(.*)</em>", "re_topic_author");

  last_topic_id_seen_ = 0;

  if (!sql_select_last_topic_id) {
    // TODO: delete sql statements
    sql_select_last_topic_id = sqlite.Statement("select last_topic_id_seen from groups where short_group_name=?");

    if (!sql_select_last_topic_id) {
      ::MessageBox(0, sqlite.LastError().c_str(), "init sql_select_last_topic_id", 0);
    }

    sql_update_last_topic_id = sqlite.Statement("update groups set last_topic_id_seen = ? where short_group_name = ?");
    if (!sql_update_last_topic_id) {
      ::MessageBox(0, sqlite.LastError().c_str(), "sql_update_last_topic_id init", 0);
    }

    sql_select_topic = sqlite.Statement("select replies_seen, interested, last_id_seen from topics where short_group_name = ? and topic_id = ?");
    if (!sql_select_topic) {
      ::MessageBox(0, sqlite.LastError().c_str(), "sql_select_topic init", 0);
    }

    sql_insert_topic = sqlite.Statement("insert into topics (short_group_name, topic_id, replies_seen, interested, last_id_seen) values (?,?,?,?,?)");
    if (!sql_insert_topic) {
      ::MessageBox(0, sqlite.LastError().c_str(), "sql_insert_topic init", 0);
    }
  }
}

void Group::ReadFromServer() {
  Topic current_topic;

  SocketClient s("discuss.joelonsoftware.com", 80);
  s.SendLine(std::string("GET /?") + short_group_name_ + " HTTP/1.0");
  s.SendLine("Host: discuss.joelonsoftware.com");
  s.SendLine("");

  if (sql_select_last_topic_id->Bind(0, short_group_name_)) {
    if (sql_select_last_topic_id->NextRow()) {
      last_topic_id_seen_ = sql_select_last_topic_id->ValueInt(0);
      sql_select_last_topic_id->Reset();
    }
    else {
      ::MessageBox(0, "sql_select_last_topic_id, NextRow()", sqlite.LastError().c_str(), 0);
    }
  }
  else {
    ::MessageBox(0, "sql_select_last_topic_id, Bind short_group_name_()", 0, 0);
  }

  bool next_is_author = false;
  bool first_topic    = true;
  while (true) {
    std::string l = s.ReceiveLine();
    if (l.empty()) break;

    if (next_is_author) {
      std::vector<std::string> v;

      if (re_topic_author->Match(l,v)) {
        current_topic.original_poster_=v[1];
      }

      topics_.push_back(current_topic);
      next_is_author = false;
    }
    else {
      std::vector<std::string> v;
      if (re_topic->Match(l, v)) {

        current_topic=CreateOrRetrieveTopic(v[1], v[2], v[3], v[4], v[5]);
        next_is_author = true;

        if (first_topic) {
          if (!sql_update_last_topic_id->Bind(0, current_topic.topic_id_)) {
            ::MessageBox(0, "sql_update_last_topic_id, Bind", sqlite.LastError().c_str(), 0);
          }
          sql_update_last_topic_id->Bind(1, short_group_name_);  
          if (!sql_update_last_topic_id->Execute()) {
            ::MessageBox(0, (std::string("Group.cpp, sql_update_last_topic_id: ") + sqlite.LastError()).c_str(), 0, 0);
          }
          first_topic = false;
        }
      }
    }
  }
}

void Group::RewindTopic() {
  cur_topic_ = topics_.begin();
}

Topic* Group::NextTopic() {
  if (cur_topic_ != topics_.end()) {
    return &(*cur_topic_++);
  }

  return 0;
}

Topic* Group::TopicWithId(std::string const& id) {
  Topic* ret;
  RewindTopic();
  while (  (ret=NextTopic())  ) {
    if (ret->topic_id_==id) return ret;
  }
  return 0;
}

Topic Group::CreateOrRetrieveTopic(
        std::string group_name,
        std::string group_id,
        std::string topic_id,
        std::string replies,
        std::string title
        ) {

  Topic ret;

  sql_select_topic->Bind(0, group_name);
  sql_select_topic->Bind(1, topic_id);

  if (sql_select_topic->NextRow()) {
    ret.interested_       = static_cast<Topic::INTERESTED>(sql_select_topic->ValueInt(1));
    ret.short_group_name_ = group_name;
    ret.group_id_         = group_id;
    ret.topic_id_         = topic_id;
    ret.replies_          = replies;
    ret.replies_seen_     = sql_select_topic->ValueString(0);
    //ret.last_id_          = "TODO: to be filled when topic items are read";
    ret.last_id_seen_     = sql_select_topic->ValueString(2);
    ret.title_            = title;

    sql_select_topic->Reset();
  }
  else {
    ret.interested_       = Topic::NA;
    ret.short_group_name_ = group_name ;
    ret.group_id_         = group_id;
    ret.topic_id_         = topic_id;
    ret.replies_          = replies;
    ret.replies_seen_     = "-1";
    ret.last_id_          = "TODO: to be filled when topic items are read";
    ret.last_id_seen_     = "0";
    ret.title_            = title;

    sql_insert_topic->Bind(0, group_name);
    sql_insert_topic->Bind(1, topic_id  );
    sql_insert_topic->Bind(2, "0"       );
    sql_insert_topic->Bind(3, Topic::NA );
    sql_insert_topic->Bind(4, "0"       );

    if (!sql_insert_topic->Execute()) {
      ::MessageBox(0, "sql_insert_topic", sqlite.LastError().c_str(), 0);
    }
  }

  return ret;
}