Saturday, April 3, 2010

The road to F#

Yesterday I got called to a meeting to talk about a prototype which I can't speak of.
However, I can talk about one requirement which is to read information from a ms project file. Things like tasks and resources. I've never parsed a ms project file before but I figured it would be similar to any other office object model using yucky COM underneath. So I fired up VS2010 and did a file -> new project on the F# node and started down the normal path of adding a reference to the ms project library blah blah blah.
Then I had to launch ms project 2007 and load a project file, and then I could go back to FSI and start to write some code. After about 2 min of hacking, here's what I got

#r @"C:\Windows\assembly\GAC\Microsoft.Office.Interop.MSProject\\Microsoft.Office.Interop.MSProject.dll"

open Microsoft.Office.Interop.MSProject

let projectapp = new ApplicationClass()
let project = projectapp.ActiveProject
for t in project.Tasks do
    printfn "%A" t.Name

Not to shabby. However, I did have to open project and load a .mpp file (which is the project file format).
This is not a big deal since the app requires it, but I thought to myself... what if it did not require it?
So I started searching around for a library or just information the file format itself to see if I could build something to parse it directly. After about 10 min I found mpxj, this library is originally written in Java and then they wrote a .NET library on top of it. So .NET interops with this library. Best part is... you can work with many versions of MS project and you don't need to have it installed. You can point to the file load it up and pull out almost any data you wish. I thought it might sound to good to be true, so I downloaded it and tried to use it in F#. After 20 min of hacking and reading the documentation... I came up with this.

#r @"local path to this file ->\mpxj.dll"
#r @"local path to this file ->\IKVM.OpenJDK.Core.dll"
#r @"local path to this file ->\IKVM.OpenJDK.Util.dll"
#r @"local path to this file ->\IKVM.OpenJDK.Beans.dll"
#r @"local path to this file ->\IKVM.OpenJDK.Charsets.dll"
#r @"local path to this file ->\IKVM.OpenJDK.jdbc.dll"
#r @"local path to this file ->\IKVM.OpenJDK.Misc.dll"
#r @"local path to this file ->\IKVM.OpenJDK.Security.dll"
#r @"local path to this file ->\IKVM.OpenJDK.SwingAWT.dll"
#r @"local path to this file ->\IKVM.OpenJDK.Text.dll"
#r @"local path to this file ->\IKVM.OpenJDK.XML.API.dll"
#r @"local path to this file ->\IKVM.OpenJDK.XML.Bind.dll"
#r @"local path to this file ->\IKVM.OpenJDK.XML.Parse.dll"
#r @"local path to this file ->\IKVM.Runtime.dll"
#r @"local path to this file ->\poi-3.6-20091214.dll"

open System
open net.sf.mpxj
open net.sf.mpxj.reader
open net.sf.mpxj.writer

let filepath = "b4ubuild_sample_07.mpp"
let preader = ProjectReaderUtility.getProjectReader(filepath)
let pfile =
let ta = pfile.getAllTasks()
let resources = pfile.getAllResources()

for i = 0 to ta.size() do
    let t = (ta.get(i) :?> Task) //down casting in F# (taking an obj and casting it to typeof(Task))
    let taskName = t.getName()  

for i = 0 to resources.size() do
    let r = (resources.get(i) :?> Resource)
    let rname = r.getName()

Now that's more like it. I've officially decided to invest in F#.  The last time I made a big investment like this was around 2005 when I first started playing with WPF. :-)