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

class Topic [JoS Reader]

Instances of this class represent topics (or, to use another word, threads) raised in one of the groups.
A Topic belongs to exactly one Group. Therefore, there is a 1:n relationship between Group and Topic.

The header file

app/Topic.h
/*
   Topic.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.

   Topic.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 TOPIC_H__
#define TOPIC_H__

/* 
   Topic.h

   Copyright (C) 2004 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.

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

*/

#include <vector>
#include <string>

#include "TopicItem.h"

class PCREWrapper;
class SQLiteStatement;

class Topic {
  public:
    Topic();

    std::string              original_poster_;
    std::string              title_;
    std::string              topic_id_;
    std::string              replies_;
    std::string              replies_seen_;

    // TODO should be called last_topic_id_ os so
    std::string              last_id_;
    std::string              last_id_seen_;

    // TODO: These two don't logically belong to Topic.h!
    //       Or do they?
    std::string              group_id_;
    std::string              short_group_name_;

    TopicItem* NextTopicItem();

    bool                     AlreadySeen();
    bool                     NewTopicItems();

    bool                     topic_items_read_;

    void                     Save();

    enum INTERESTED {
      YES,
      NO,
      NA
    };
    INTERESTED               interested_;

  private:

    void Init();

    void ReadTopicItems();

    std::vector<TopicItem>   topic_items_;
    int                      read_up_to_;

    bool                     is_iterating_;
    std::vector<TopicItem>::iterator cur_topic_item_;

    static PCREWrapper*      re_topic_item_start;
    static PCREWrapper*      re_topic_item_end;
    static PCREWrapper*      re_topic_item_author_start;
    static PCREWrapper*      re_topic_item_author_end;
    static PCREWrapper*      re_topic_item_id;

    static SQLiteStatement*  sql_update_topic;
};

#endif

The implementation file

app/Topic.cpp
/*
   Topic.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.

   Topic.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 "Topic.h"
#include "Socket.h"
#include "PCREWrapper.h"
#include "stdhelpers.h"

#include "JOSReader.h"

PCREWrapper* Topic::re_topic_item_start         = 0;
PCREWrapper* Topic::re_topic_item_end           = 0;
PCREWrapper* Topic::re_topic_item_author_start  = 0;
PCREWrapper* Topic::re_topic_item_author_end    = 0;
PCREWrapper* Topic::re_topic_item_id            = 0;

SQLiteStatement* Topic::sql_update_topic        = 0;

Topic::Topic() {
  Init();
}

void Topic::ReadTopicItems() {
  SocketClient s("discuss.joelonsoftware.com", 80);
  s.SendLine(std::string("GET /default.asp?" + short_group_name_ + "." + group_id_ + "." + topic_id_ + "." + replies_ + " HTTP/1.0"));
  s.SendLine("Host: discuss.joelonsoftware.com");
  s.SendLine("");

  bool in_topic_item        = false;
  bool in_topic_item_author = false;

  TopicItem curTopicItem;
  curTopicItem.Reset();

  std::string last_line;
  while (true) {
    std::string l = s.ReceiveLine();
    if (l.empty()) break;

    std::vector<std::string> v;

    if (in_topic_item) {
      if (re_topic_item_end->Match(l, v)) {
        in_topic_item = false;
      }
      else {
        curTopicItem.html_+=l;
      }
    }
    else {
      if (re_topic_item_id->Match(l, v)) {
        last_id_        =v[1];
        curTopicItem.id_=last_id_;
      }
      if (re_topic_item_start->Match(l, v)) {
        in_topic_item=true;
      }
      else if (re_topic_item_author_start->Match(l, v)) {
        in_topic_item_author=true;
      }
    }

    if (in_topic_item_author) {
      if (re_topic_item_author_end->Match(l, v)) {
        in_topic_item_author = false;

        curTopicItem.author_=v[1];

        if (v[2] == "<img src=\"greencheck.gif\"") {
          curTopicItem.recognized_author_ = true;
        }
        topic_items_.push_back(curTopicItem);
        curTopicItem.Reset();
      }
    }
  }
  topic_items_read_=true;
}

TopicItem* Topic::NextTopicItem() {
  if (!topic_items_read_) ReadTopicItems();

  if (! is_iterating_) {
    is_iterating_=true;
    cur_topic_item_ = topic_items_.begin();
  }

  if (cur_topic_item_ != topic_items_.end()) {
    return &(*cur_topic_item_++);
  }

  is_iterating_=false;
  return 0;
}

void Topic::Init() {
  is_iterating_    =false;
  topic_items_read_=false;

  if (!re_topic_item_start) {

    //TODO: delete re_topic_item_start
    re_topic_item_start = new PCREWrapper("<div class=\"discussBody\">", "re_topic_item_start");

    //TODO: delete re_topic_item_start
    re_topic_item_end   = new PCREWrapper("^\t\t\t</div", "re_topic_item_end");
    
    //TODO: delete re_topic_item_author_start
    re_topic_item_author_start = new PCREWrapper("<div class=\"discussSign\">", "re_topic_item_author_start");

    //TODO: delete re_topic_item_author_end
    re_topic_item_author_end   = new PCREWrapper("^\\t\\t\\t\\t\\t([^<]*).*(<br />|<img src=\"greencheck.gif\")", "re_topic_item_author_end");

    //TODO: delete re_topic_item_id
    re_topic_item_id           = new PCREWrapper("^\\t\\t\\t<a name=\"discussTopic(\\d+)\">", "re_topic_item_id");

    sql_update_topic = sqlite.Statement("update topics set replies_seen = ?, interested = ?, last_id_seen = ? where short_group_name = ? and topic_id = ?");
  }
}

void Topic::Save() {
  replies_seen_ = replies_;
  last_id_seen_ = last_id_;
  sql_update_topic->Bind(0, replies_seen_    );
  sql_update_topic->Bind(1, interested_      );
  sql_update_topic->Bind(2, last_id_seen_    );
  sql_update_topic->Bind(3, short_group_name_);
  sql_update_topic->Bind(4, topic_id_        );

  sql_update_topic->Execute();
}

bool Topic::NewTopicItems() {
  if (To<int>(replies_seen_ ) < To<int>(replies_)) return true;  
  return false;
}