Posts Tagged ‘Object’

Toodledo API (simple java version)

Sunday, June 5th, 2011

Wanted to get a list of all the bugs for my Star Trek game, that I have registered at Toodledo and have them listed on the Trekwar wiki.

Toodledo offers a nicely done API, but there was no java implementation, there were a couple of unofficial ones, but I decided to write my own very simple basic program for connecting to Toodledo and getting all the tasks as XML, and parsing them into a simple Java object. So if you’re doing something similar, this program might be a nice place to start. To keep it as short as possible, comments and exception handling is pretty much not there :)

download the file: Toodledo2html.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
 
class Task {
    String id;
    String title;
    String modified;
    String completed;
 
    String folder;
    String context;
    String tag;
    String status;
    String priority;
    String length;
    String note;
}
 
public class Toodledoo2html {
    // TODO you must get the 4 values below from Toodledo
    private static String appID = "x";
    private static String userID = "x";
    private static String userPW = "x";
    private static String applicationToken = "x";
 
    private static String fields = "folder,context,tag,status,priority,length,note"; // get these fields when downloading tasks
 
    private static String sessionToken;
    private static String key;
 
    private static String toodledoTokenURL = "https://api.toodledo.com/2/account/token.php";
    private static String toodledoGetTaskURL = "https://api.toodledo.com/2/tasks/get.php";
 
    public static void main(String[] args) {
        sessionToken = getSessionToken(md5(userID+applicationToken));
        key = md5(md5(userPW)+applicationToken+sessionToken);
        ArrayList<Task> tasks = getTasks();
        // do whatever to tasks
    }
 
    private static ArrayList<Task> getTasks() {
        try {
            String data = "?key=" + key + ";fields=" + fields + ";f=xml";
 
            System.out.println("Getting tasks: " + toodledoGetTaskURL + data);
 
            URL url = new URL(toodledoGetTaskURL + data);
            URLConnection connection = url.openConnection();
            connection.setDoOutput(true);
 
            BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            StringBuilder xml = new StringBuilder();
            String line = in.readLine();
            while( line != null) {
                System.out.println(line);
                xml.append(line);
                line = in.readLine();
            }
            in.close();
 
            ArrayList<Task> tasks = xmlToTasks(xml.toString());
 
            for(Task t : tasks) {
                System.out.println(t.id + " " + t.title);
            }
            return tasks;
 
        } catch (MalformedURLException ex) {
            System.out.println(ex);
        } catch (IOException ioe) {
            System.out.println(ioe);
        }
        return null;
    }
 
    private static ArrayList<Task> xmlToTasks(String xml) {
        ArrayList<Task> taskList = new ArrayList<Task>();
 
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(new InputSource(new StringReader(xml)));
 
            Element root = doc.getDocumentElement();
 
            NodeList tasks = root.getElementsByTagName("task");
            for (int i = 0; i < tasks.getLength(); i++) {
                Element task = (Element) tasks.item(i);
 
                Task t = new Task();
                t.id = getDomTextValue(task, "id");
                t.title = getDomTextValue(task, "title");
                t.modified = getDomTextValue(task, "modified");
                t.completed = getDomTextValue(task, "completed");
 
                t.folder = getDomTextValue(task, "folder");
                t.context = getDomTextValue(task, "context");
                t.tag = getDomTextValue(task, "tag");
                t.status = getDomTextValue(task, "status");
                t.priority = getDomTextValue(task, "priority");
                t.length = getDomTextValue(task, "length");
                t.note = getDomTextValue(task, "note");
                taskList.add(t);
            }
 
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(Toodledoo2html.class.getName()).log(Level.SEVERE, null, ex);
        }
        catch(SAXException se) {
 
        }
        catch(IOException ioe) {
 
        }
 
        System.out.println("xmlToTasks got " + taskList.size() + " tasks");
        return taskList;
    }
 
    private static String getSessionToken(String signature) {
        try {
            String data = "?userid=" + userID + ";appid=" + appID + ";sig=" + signature + ";f=xml";
 
            System.out.println("Getting session token: " + toodledoTokenURL+data);
 
            URL url = new URL(toodledoTokenURL + data);
            URLConnection connection = url.openConnection();
            connection.setDoOutput(true);
 
            BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String xml = in.readLine();
            in.close();
            String token = xml.substring(xml.indexOf("<token>")+7, xml.indexOf("</token>"));
            return token;
        } catch (MalformedURLException ex) {
            System.out.println(ex);
        }
        catch(IOException ioe) {
            System.out.println(ioe);
        }
        return "";
    }
 
    private static String md5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(input.getBytes());
 
            byte byteData[] = md.digest();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < byteData.length; i++) {
                sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException nse) {
            System.err.println("ERROR. MD5 ALGORITHM NOT FOUND");
            return "";
        }
    }
 
    public static String getDomTextValue(Element element, String tag) {
        if (element != null) {
            NodeList nl = element.getElementsByTagName(tag);
            if (nl != null && nl.getLength() > 0) {
                Element el = (Element) nl.item(0);
                if(el != null && el.getFirstChild() != null) {
                    return el.getFirstChild().getNodeValue();
                }
            }
        }
        return "";
    }
}

Note: This program was made to run once per hour.. running this program many times in a row will cause Toodledo to ban you for about 1 hour. When that happens there will be a IndexOutOfBoundsException -7 from the method that gets the session token. This can be fixed by saving the token you get between requests, and not asking for a new one each time (which is what causes the short ban).

Oups

Monday, March 15th, 2010

While cleaning up in the core classes of Trekwar yesterday (documenting functions, formatting/structuring code, minor improvements) I also saw lots of equals(Object o) methods. And in my infinite wisdom I decided to use generics since it is used pretty extensively throughout the code anyway, so I changed basically all the equals methods.

example:

public boolean equals(Object o) {
        if(o instanceof Structure)
            return equals((Structure)o);
        return false;
    }

was changed to:

public boolean equals(Structure s2) {
        return name.equals(s2.getName());
    }

Now this is not a problem when writing code, as you pretty much always compare objects of the same type. However, the code also uses the Java Collections contains() method a few places, and it always uses the equals(Object) version. So when my classes no longer provided this method, the default one inherited from java.lang.Object was used instead. This caused a strange array of bugs to appear (star systems on the map not shown with faction color/icon except for the Cardassians for some strange reason, users not having any technologies, unable to build any structures, generally erratic behavior, etc..). Luckily I discovered the cause pretty quickly and only wasted about an hour or so on this :)

I guess the lesson is that objects that needs to be compared, directly or indirectly by contains() or other methods, should always define an equals(Object) method that just passes the call along to the equals method written with generics.

public boolean equals(Object o) {
        if(o instanceof Structure) {
            return equals((Structure)o);
        }
        return false;
    }

And perhaps that you should not mess around with your code for hours on end without stopping to test if you broke it once in a while :)