Serbian Cafe Forums Reader by Mihailo Despotovic, September 2001

The Serbian Cafe forums closed permanently on December 3, 2024. Offline example: the forum list is the real one from 2001, but the topics, threads and usernames are made up. Pick a topic number to read its thread.


    
The original Java code, 8 files
screader/Constants.java
package screader;

public class Constants
{
  public static final String MAIN_TITLE = "Serbian Cafe Forums Reader";

  public static final String SC_URL = "http://www.serbiancafe.com";
  public static final String SC_ROOT = SC_URL + "/diskusije/";
  public static final String KOSARKA_POSTFIX = "/diskusije/mesg/55/?1";
  public static final String MSG_HEADER = "<a href=\"/diskusije/mesg";
  public static final int MSG_OFFSET = "<a href=\"".length();
}
screader/ContentGetter.java
package screader;

import java.io.*;
import java.net.*;

public class ContentGetter
{
  public static final int TREE = 0;
  public static final int TOPICS = 1;
  public static final int SINGLE_TOPIC = 2;

  public static String getContent(String url, int what)
  {
    try
    {
      BufferedReader br = null;
      try
      {
        URL u = new URL(url);
        URLConnection c = u.openConnection();
        InputStreamReader sr = new InputStreamReader(c.getInputStream());
        br = new BufferedReader(sr);
      }
      catch(UnknownHostException uhe)
      {
        String fname = null;
        switch(what)
        {
          case(TOPICS) :
          {
            fname = "c:\\mihailo\\screader\\screader\\kosarkatopics.txt";
            break;
          }
          case(SINGLE_TOPIC) :
          {
            fname = "c:\\mihailo\\screader\\screader\\singletopic.txt";
            break;
          }
          case(TREE) :
          {
            fname = "c:\\mihailo\\screader\\screader\\tree.txt";
            break;
          }
          default : throw new IllegalArgumentException("Unknown type");
        }

        br = new BufferedReader(new FileReader(new File(fname)));
      }

      StringBuffer sb = new StringBuffer("");
      String line = null;
      while((line = br.readLine()) != null) sb.append(line);
      return sb.toString();
    }
    catch(Exception e)
    {
      return "ERROR: " + e.toString();
    }
  }
}
screader/ContentParser.java
package screader;

import java.util.*;

public class ContentParser
{
  public Vector topics = null;

  public void parseTopics(String all)
  {
    topics = new Vector();
    Topic t = null;
    if(all.startsWith("ERROR"))
    {
      t = new Topic();
      t.title = all;
      topics.addElement(t);
      return;
    }
    StringBuffer sb = new StringBuffer();
    int start, end = 0;
    String token = null;
    while(all.indexOf(Constants.MSG_HEADER) > 0)
    {
      t = new Topic();

      start = all.indexOf(Constants.MSG_HEADER) + Constants.MSG_OFFSET;
      all = all.substring(start, all.length());
      end = all.indexOf(">") - 1;
      t.url = all.substring(0, end);
      t.url = Constants.SC_URL + t.url;

      start = all.indexOf("/diskusije/mesg/");
      all = all.substring(start, all.length());
      start = all.indexOf(">") + 1;
      all = all.substring(start, all.length());
      end = all.indexOf("</a>");
      token = all.substring(0, end);
      t.title = token;

      start = all.indexOf("<b>") + "<b>".length();
      end = all.indexOf("</b>");
      token = all.substring(start, end);
      t.author = token;

      start = all.indexOf("<font size=-1>") + "<font size=-1>".length() + 1;
      end = all.indexOf("</font>") - 1;
      int newLine = all.indexOf("<br");
      if(newLine > 0 && newLine > start && newLine > end)
      {
        token = all.substring(start, end);
        t.replies = Integer.parseInt(token);
      }
      else
      {
        t.replies = 0;
      }

      topics.addElement(t);
    }

    // next?
    if(all.indexOf("NEXT") > 0)
    {
      start = all.indexOf("<a href");
      end = all.indexOf("\">");
      t = new Topic();
      t.title = "";
      t.author = "";
      t.replies = -1;
      t.url = all.substring(start + Constants.MSG_OFFSET, end);
      topics.addElement(t);
    }
  }

  public String parseSingleTopic(String all)
  {
    //System.out.println("all=" + all);
    if(all.indexOf("<blockquote>") < 0)
      return "Sorry.  Cannot parse the topic..."; // something is wrong

    StringBuffer resb = new StringBuffer("");
    String temp = "";
    boolean first = true;

    while(all.indexOf("<blockquote>") >= 0)
    {
      int start = all.indexOf("<blockquote>") + "<blockquote>".length();
      if(first) start += "<b><font color=green>".length();
      else start += "<br><b>".length();
      all = all.substring(start, all.length());
      int end = all.indexOf("</blockquote>");
      temp = all.substring(0, end);

      // get rid of email
      if(temp.startsWith("<a href"))
      {
        start = temp.indexOf(">") + 1;
        end = temp.indexOf("</a>");
        temp = temp.substring(start, end) +
               temp.substring(end + "</a>".length(), temp.length());
      }

      start = temp.indexOf("<p>");
      temp = temp.substring(0, start) + "\n" +
             temp.substring(start + "<p>".length(), temp.length());

      if(first) temp = Util.replaceStrings(temp, "</font></b>", "");
      temp = Util.cleanString(temp);

      resb.append(temp);
      resb.append("\n__________________________________________\n\n");

      first = false;
    }

    return resb.toString();
  }
}
screader/MainJFrame.java
package screader;

import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class MainJFrame extends JFrame
  implements ActionListener, MouseListener
{
  private JSplitPane treeAndTopics = null;
  private JSplitPane upAndDown = null;
  private Insets insets2205 = new Insets(2, 2, 0, 5);
  private Border emptyBorder = BorderFactory.createEmptyBorder();
  private ContentParser cp = new ContentParser();
  private JTextArea ta = new JTextArea();
  private JTree jt = null;

  public MainJFrame()
  {
    super();

    this.addWindowListener( new WindowAdapter()
       { public void windowClosing(WindowEvent e) { System.exit(0); } } );
    Container c = this.getContentPane();
    c.setLayout(new BorderLayout());

    JPanel treePanel = new JPanel();
    JPanel topicsPanel = new JPanel();
    JPanel threadPanel = new JPanel();
    threadPanel.setLayout(new BorderLayout());
    threadPanel.add(ta, BorderLayout.CENTER);
    ta.setEditable(false);
    ta.setBackground(Color.yellow);
    ta.setLineWrap(false);
    ta.setDoubleBuffered(true);
    ta.setLineWrap(true);
    ta.setMargin(new Insets(5, 5, 5, 20));

    JScrollPane jsp2 = new JScrollPane(topicsPanel);
    topicsPanel.setLayout(new GridBagLayout());
    GridBagConstraints gbc = new GridBagConstraints();

    JScrollPane jsp3 = new JScrollPane(threadPanel);

    treeAndTopics = new JSplitPane
      (JSplitPane.HORIZONTAL_SPLIT, treePanel, jsp2);
    upAndDown = new JSplitPane
      (JSplitPane.VERTICAL_SPLIT, treeAndTopics, jsp3);

    treeAndTopics.setOneTouchExpandable(true);
    treeAndTopics.setContinuousLayout(true);
    upAndDown.setOneTouchExpandable(true);
    upAndDown.setContinuousLayout(true);

    c.add(upAndDown, BorderLayout.CENTER);

    jt = new JTree(TreeProducer.produceTree());
    jt.addMouseListener(this);
    treePanel.setLayout(new BorderLayout());
    JScrollPane jsp = new JScrollPane(jt);
    treePanel.add(jsp);

    Topic t = null;
    cp.parseTopics(ContentGetter.getContent
    (Constants.SC_URL + Constants.KOSARKA_POSTFIX, ContentGetter.TOPICS));
    if(cp.topics.size() == 0)
    {
      gbc.gridx = 0; gbc.gridy = 0;
      JLabel l = new JLabel("No topics!");
      topicsPanel.add(l, gbc);
      return;
    }
    String rstring = null;
    JButton b = null;
    JPanel ppp = null;
    for(int i=0; i<cp.topics.size(); i++)
    {
      t = (Topic)cp.topics.elementAt(i);
      b = new JButton("" + (i+1));
      b.addActionListener(this);
      if(t.replies <= 0) rstring = "";
      else if(t.replies == 1) rstring = "reply";
      else rstring = "replies";
      JTextField tf = new JTextField(t.title + " -- " + t.author + " " +
        (t.replies == 0 ? "" : "(" + t.replies + " " + rstring + ")"));
      tf.setBorder(emptyBorder);
      tf.setEditable(false);
      if(t.title.startsWith("ERROR"))
      {
        topicsPanel.setLayout(new BorderLayout());
        topicsPanel.add(new JLabel(t.title));
        break;
      }
      else if(t.replies == -1)
      {
        b.setText("Next 40");
        gbc.fill = gbc.NONE;
        gbc.gridx = 1; gbc.gridy = i; gbc.anchor = gbc.WEST;
        topicsPanel.add(b, gbc);
      }
      else
      {
        gbc.gridx = 0; gbc.gridy = i; gbc.anchor = gbc.WEST;
        gbc.fill = gbc.BOTH; gbc.insets = insets2205;
        topicsPanel.add(b, gbc);
        gbc.gridx = 1; gbc.gridy = i; gbc.anchor = gbc.WEST;
        topicsPanel.add(tf, gbc);
      }
    }

    setGuiStuff();
    this.setVisible(true);
  }

  private void setGuiStuff()
  {
    int w = (int)(Toolkit.getDefaultToolkit().getScreenSize().getWidth() * 0.90);
    int h = (int)(Toolkit.getDefaultToolkit().getScreenSize().getHeight() * 0.90);
    int x = (int)((Toolkit.getDefaultToolkit().getScreenSize().getWidth() - w) / 2);
    int y = (int)((Toolkit.getDefaultToolkit().getScreenSize().getHeight() - h) / 2);
    this.setBounds(x, y, w, h);
    this.setTitle(Constants.MAIN_TITLE);

    treeAndTopics.setDividerLocation((int)(0.25 * getWidth()));
    upAndDown.setDividerLocation((int)(0.66 * getHeight()));

    treeAndTopics.setDoubleBuffered(true);
    upAndDown.setDoubleBuffered(true);
  }

  public void actionPerformed(ActionEvent ae)
  {
    if(ae.getSource() instanceof JButton)
    {
      String label = ((JButton)ae.getSource()).getText();
      try
      {
        int n = Integer.parseInt(label) - 1;
        setCursor(new Cursor(Cursor.WAIT_CURSOR));
        String text = cp.parseSingleTopic(ContentGetter.getContent
          (((Topic)cp.topics.elementAt(n)).url, ContentGetter.SINGLE_TOPIC));
        //ta.setText(Util.formatText(text, 80));
        ta.setText(text);
        ta.setCaretPosition(0);
        setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
      }
      catch(NumberFormatException nfe) { }
      if(label.indexOf("ext") > 0)
      {
        System.out.println("call next: " +
          ((Topic)cp.topics.elementAt(cp.topics.size() - 1)).url);
      }
    }
  }

  public void mouseClicked(MouseEvent me)
  {
    String key = null;
    try
    {
      key =  jt.getSelectionPath().getLastPathComponent().toString();
    }
    catch(Exception e) { /* ignore */ }
    if(key != null && key.length() > 0 &&
       TreeProducer.ht.get(key) != null &&
       TreeProducer.ht.get(key).toString().length() > 0)
    {
      System.out.println("CALL: " + TreeProducer.ht.get(key));
    }
  }
  public void mouseExited(MouseEvent me) {}
  public void mouseEntered(MouseEvent me) {}
  public void mouseReleased(MouseEvent me) {}
  public void mousePressed(MouseEvent me) {}
}
screader/SCR.java
package screader;

import java.net.*;
import java.io.*;
import java.util.*;
import java.awt.*;

public class SCR
{ 
  public static void main(String[] a)
  {
    try
    { 
      MainJFrame mf = new MainJFrame();
    }
    catch(Exception e)
    {
      e.printStackTrace();
    } 
  }
}
screader/Topic.java
package screader;

import java.awt.*;
import java.awt.event.*;

public class Topic
{
  public String title = "";
  public String author = "";
  public int replies = 0;
  public String url = "";
}
screader/TreeProducer.java
package screader;

import javax.swing.*;
import javax.swing.tree.*;
import java.util.*;

public class TreeProducer
{
  public static Hashtable ht = new Hashtable();

  public static TreeNode produceTree()
  {
    String content = ContentGetter.getContent
    (Constants.SC_ROOT, ContentGetter.TREE);

    DefaultMutableTreeNode root = new DefaultMutableTreeNode("Serbian Cafe");
    DefaultMutableTreeNode branch = null;
    DefaultMutableTreeNode node = null;

    String name = null;
    String url = null;

    int ahref = 0;
    int subh = 0;
    int st = 0;
    int end = 0;
    int size = 0;

    content = Util.replaceStrings
      (content, "<a href=/diskusije", "<a href=\"/diskusije");

    while(true)
    {
      ahref = content.indexOf(Constants.MSG_HEADER);
      subh = content.indexOf("<b class=subheading>");

      if(ahref < 0) break;

      // main topics that are not hyperlinked
      if((subh > 0) && (subh < ahref))
      {
        st = subh + "<b class=subheading>".length();
        end = content.indexOf("</b>");
        name = content.substring(st, end);
        url = "";
        ht.put(name, url);
        branch = new DefaultMutableTreeNode(Util.cleanString(name));
        root.add(branch);
        content = content.substring
          (end + "</b>".length(), content.length());
        continue;
      }

      st = content.indexOf(Constants.MSG_HEADER);
      String temp = content.substring(st, content.length());
      size = temp.indexOf("</a>");
      name = temp.substring(0, size);

      url = Util.getFirstUrl(name);
      url = Constants.SC_URL + url;

      if(name.indexOf("<b class=subheading>") >= 0)
      {
        int start1 = name.indexOf("=subheading>") + "=subheading>".length();
        int end1 = name.indexOf("</b>");
        name = name.substring(start1, end1);
        ht.put(name, url);
        branch = new DefaultMutableTreeNode(Util.cleanString(name));
        root.add(branch);
      }
      else
      {
        int start1 = name.indexOf(">") + 1;
        name = name.substring(start1, name.length());
        node = new DefaultMutableTreeNode(Util.cleanString(name));
        ht.put(name, url);
        if(branch != null) branch.add(node);
        else root.add(node);
      }

      content = content.substring
        (st + size + 1 + "</X>".length(), content.length());
    }

    return root;
  }
}
screader/Util.java
package screader;

public class Util
{
  // replace all occurences of s1 with s2 in s
  public static String replaceStrings(String s, String s1, String s2)
  {
    if (s == null || s1 == null || s2 == null || s1.equals("")) return s;

    StringBuffer res = new StringBuffer();
    int s1_len = s1.length();

    for (int i = 0; i < s.length(); i++)
    {
      if ((i <= s.length() - s1_len) &&
          (s.substring(i, i + s1_len).equals(s1)))
      {
        res.append(s2);
        i += s1_len - 1;
      }
      else
      {
        res.append(s.charAt(i));
      }
    }
    return res.toString();
  }

  // cleans string for gui display
  public static String cleanString(String s)
  {
    s = replaceStrings(s, "<b>", "");
    s = replaceStrings(s, "<B>", "");
    s = replaceStrings(s, "</b>", "");
    s = replaceStrings(s, "</B>", "");

    s = replaceStrings(s, "<br>", "\n");
    s = replaceStrings(s, "<BR>", "\n");
    s = replaceStrings(s, "<Br>", "\n");
    s = replaceStrings(s, "<bR>", "\n");

    return s;
  }

  // returns the first url pointer in the string
  public static String getFirstUrl(String s)
  {
    int start1 = s.indexOf("<a href=") + "<a href=".length();
    int end1 = s.indexOf(">");
    String url = s.substring(start1, end1);
    url = Util.replaceStrings(url, "\"", "");
    url = Util.replaceStrings(url, "//", "/");
    return url;
  }

  public static String formatText(String s, int cols)
  {
    String temp = "";
    while(true)
    {
      if(s.length() <= cols) break;
      temp += s.substring(0, cols);
      temp += "\n";
      s = s.substring(cols, s.length());
    }
    return temp;
  }
}