Posts Tagged ‘Programming’

SOAP on the Android Platform

Tuesday, March 23rd, 2010

I’ve been meaning to put a post about this some time back but have either been too busy or too forgetful to do so. A few months ago I was tasked with creating a somewhat basic mobile app on the Android Platform. Google seems to have a way of making their APIs, platforms, and languages really easy to learn and use. Unfortunately, and understandably, Google did not provide native SOAP support in their Android APIs. Thankfully, interfacing with a SOAP based web service is still possible, albeit a bit frustrating thanks to kSOAP2 for Android.

However, either due to my ignorance or something else using kSOAP2 isn’t exactly a usable, production ready solution out of the box. It is certainly not as easy to use or elegant as using Visual Studio to build applications which consume WCF services. A lot of foot work needs to be done to get everything setup and running (and that’s not even running correctly, I still haven’t gotten marshaling to work properly).

To begin, modern versions of Android require the following line in the manifest in order for the application to have access to the internet. Otherwise, it may seem like everything has been done correctly but the code will still throw exceptions due to the OS denying it access to the internet.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="org.example.android"
      android:versionCode="1"
      android:versionName="1.0">

   …
   <uses-permission android:name="android.permission.INTERNET" />   
   …
</manifest>

Second, ensure that you know the namespace, url, default action URI, and the version of SOAP your web service is using. In my case I assumed it to be 2.0, then tried 1.2, then tried 1.0, until I looked into the source code for kSOAP2 and examined the SOAP packets coming back from my WCF service until I realized I was using 1.1. If you don’t tell kSOAP2 which version of SOAP it will not work. As for the other values, place them in an config file so code doesn’t need to be modified when they changed. I made an application_config.xml in res/values in my project with the following:

<?xml version="1.0" encoding="utf-8"?>
<resources>
        <string name="WCFNamespace">urn:http://example.org/myservice/mysendpoint/2009/12/25</string>
        <string name="WCFUrl">http://example.org/MyService.svc/EndPoint</string>
        <string name="WCFDefaultActionURI">urn:http://example.org/myservice/endpoint/2009/12/25/IEndPoint/</string>
</resources>

I was originally going to make classes for each service call but opted not to. However, I still kept the interface so the next few code snippets comprise more or less the entirety of the “communication layer.”

package org.example.android.comm.soap;

import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.AndroidHttpTransport;

//Interface for objects which communicate with SOAP messages
public interface ISoapCommunicable {        
         SoapObject getSoapRequest();
         SoapSerializationEnvelope getSoapEnvelope();
         AndroidHttpTransport getTransport();
         boolean makeSoapRequest();
         Object getSoapResponse();
}

package org.example.android.comm.soap;

import java.io.IOException;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.SoapFault;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.AndroidHttpTransport;
import org.xmlpull.v1.XmlPullParserException;

import android.content.Context;
import org.example.android.R;

//a class that will act as a service proxy to the SOAP service
public class SoapHelper implements ISoapCommunicable { 
        protected String METHOD_NAME = null;
        protected String SOAP_ACTION = null;
        protected String NAMESPACE = null;
        protected String URL = null;
        protected Context context;
        protected boolean debug;
       
        SoapObject request = null;
        AndroidHttpTransport transport = null;
        SoapSerializationEnvelope envelope = null;
       
        public SoapHelper (Context context, boolean debug) {
                this.context = context;  
                this.NAMESPACE = this.context.getString(R.string.WCFNamespace);    
                this.URL = this.context.getString(R.string.WCFUrl);
                this.debug = debug;
        }
       
        public SoapHelper (Context context) {
                this(context, false);            
        }       
       
        public SoapObject getSoapRequest () {
                if (this.request == null) {
                        this.request = new SoapObject(NAMESPACE, METHOD_NAME);
                }
                return this.request;
        }
        public SoapSerializationEnvelope getSoapEnvelope () {
                if (this.envelope == null) {
                        this.envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
                        this.envelope.dotNet = true;
                        this.envelope.setOutputSoapObject(this.getSoapRequest());
                        this.addMappingsAndMarshals(this.envelope);
                }              
        return this.envelope;
        }
        public AndroidHttpTransport getTransport() {
                if (this.transport == null) {
                        this.transport = new AndroidHttpTransport(URL);
                }
                this.transport.debug = this.debug;
                return this.transport;
        }
        public boolean makeSoapRequest () {          
                try {
                        if(SOAP_ACTION == null) {
                                SOAP_ACTION = this.context.getString(R.string.WCFDefaultActionURI) + METHOD_NAME;
                        }
                        this.getTransport().call(SOAP_ACTION, this.getSoapEnvelope());
                        return true;
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                } catch (XmlPullParserException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                } catch (Exception e) {
                        e.printStackTrace();
                }
                return false;
        }
        public Object getSoapResponse() {
                try {
                        return this.getSoapEnvelope().getResponse();
                } catch (SoapFault e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
                return null;
        }
       
        private void addMappingsAndMarshals(SoapSerializationEnvelope envelope) {
                EventDetailsMarshal marshal = new EventDetailsMarshal();
                marshal.register(envelope);
        }
       
        public void setMethodName (String method) {
                this.METHOD_NAME = method;
        }
       
}

package org.example.android.webservice.mobile;

import org.ksoap2.serialization.KvmSerializable;

//base class for any webservice object
public abstract class BaseWebserviceObject implements KvmSerializable { 
        public BaseWebserviceObject () {
                super();               
        }       
}

package org.example.android.webservice.mobile;

import java.util.Date;
import java.util.Hashtable;

import org.kobjects.isodate.IsoDate;
import org.ksoap2.serialization.MarshalDate;
import org.ksoap2.serialization.PropertyInfo;

//a class to represent events pulled from or pushed to the web service
public class EventDetails extends BaseWebserviceObject {
        public static Class CLASS = new EventDetails().getClass();     
        private int id;
        private String name;
        private Date startDate;
        private Date endDate;      
               
        public Object getProperty (int idx) {
                Object rtn;
                switch (idx) {
                        case 0 :
                                rtn = this.endDate;
                                break;
                        case 1 :
                                rtn = this.id;
                                break;
                        case 2 :
                                rtn = this.name;
                                break;
                        case 3 :
                                rtn = this.startDate;
                                break;
                        default :
                                rtn = null;
                                break;
                }
                return rtn;
        }

        public int getPropertyCount () {       
                return 4;
        }

        public void getPropertyInfo (int idx, Hashtable properties, PropertyInfo info) {
                switch (idx) {
                        case 0 :
                                info.type = MarshalDate.DATE_CLASS;
                                info.name = "EndDate";
                                break;
                        case 1 :
                                info.type = PropertyInfo.INTEGER_CLASS;
                                info.name = "ID";
                                break;
                        case 2 :
                                info.type = PropertyInfo.STRING_CLASS;
                                info.name = "Name";
                                break;
                        case 3 :
                                info.type = MarshalDate.DATE_CLASS;
                                info.name = "StartDate";
                                break;
                        default :
                                break;
                }

        }

        public void setProperty (int idx, Object value) {              
                switch (idx) {
                        case 0 :
                                this.endDate = IsoDate.stringToDate(value.toString(), IsoDate.DATE_TIME);
                                break;
                        case 1 :
                                this.id = Integer.parseInt(value.toString(), 10);
                                break;
                        case 2 :
                                this.name = value.toString();
                                break;
                        case 3 :
                                this.startDate = IsoDate.stringToDate(value.toString(), IsoDate.DATE_TIME);
                                break;
                        default :
                                break;
                }
        }       
       
        public int getId() {
                return this.id;
        }
       
        public Date getStartDate() {
                return new Date(this.startDate.toString());
        }
       
        public Date getEndDate() {           
                return new Date(this.endDate.toString());
        }
       
        public String getName() {
                return this.name;
        }
       
        public void setId(int id) {
                this.id = id;
        }       
       
        public void setStartDate(String date) {
                this.startDate = IsoDate.stringToDate(date, IsoDate.DATE_TIME);
        }
       
        public void setEndDate(String date) {      
                this.endDate = IsoDate.stringToDate(date, IsoDate.DATE_TIME);
        }
       
        public void setName(String name) {
                this.name = name;
        }
}

Finally, after all that in my activity I added a method which retrieves all events from the service with specified parameters. The code is as follows:

private boolean getEvents(int userId) {
        SoapHelper service = new SoapHelper(this.getApplicationContext());       
        service.setMethodName("GetEvents");

        SoapObject request = service.getSoapRequest();     
       
        request.addProperty("userID", userId);                                    
        boolean rtn = false;
       
        if (service.makeSoapRequest()) {
            try {                                      
                SoapObject results = (SoapObject) service.getSoapResponse();
               
                this.events = EventDetailsMarshal.parseEventDetails(results);
                                                       
            } catch (Exception e) {
                e.printStackTrace();               
            }                  
            rtn = true;
        }
       
        return rtn;
    }

Since I could not get marshaling to work in kSOAP2 I added a parse method in the EventDetailsMarshal class. The interesting bits are:

private static EventDetails parseEvent(SoapObject response) {
                EventDetails event = new EventDetails();
               
                for (int i = 0; i < response.getPropertyCount(); i++) {
                        event.setProperty(i, response.getProperty(i));
                }
                return event;
        }
       
        public static Vector<EventDetails> parseEventDetails(SoapObject response) {
                Vector<EventDetails> events = new Vector<EventDetails>();
               
                int propertySize = response.getPropertyCount();
               
                for(int i = 0; i < propertySize; i++) {
                        PropertyInfo pi = new PropertyInfo();
                        response.getPropertyInfo(i, pi);
                        String tagName = pi.getName();
                        if(tagName.equals("EventDetails")) {
                                Object o = response.getProperty(i);
                                if(response.getProperty(i) instanceof SoapObject) {
                                        events.add(parseEvent((SoapObject)o));
                                }
                        }
                }
               
               
                return events;
        }

With this it is now possible to communicate with a SOAP service, pass it arguments, and retrieve results. However, this code is far from production ready. Given more time I would have created something akin to Visual Studio’s code generator when it connects to SOAP services to create the classes for types used by the service and setup marshaling properly. However, this is a stepping stone in the right direction.

If you have to make Android connect to a WCF or other SOAP based service, I hope this helps.

The Label Element

Friday, December 11th, 2009

Dear Web Developers,

The following mark-up is commonly used to put text adjacent to an input element such as a radio button or check box:

<input name="foo" type="checkbox" value="bar" /> Click this checkbox.

DO NOT DO THIS!

Instead wrap put the text next to the check box inside the label element and give the input element an ID the label element can reference. e.g.

<input id="foo" name="foo" type="checkbox" value="bar" />
<label for="foo">Click this checkbox.</label>

This allows for the user to click on the text to interact with the input element as well as clicking just on the input element itself increasing usability of your sites and applications and subduing my rage.

Code Style is not Subjective p.2

Monday, August 10th, 2009

Now for a bit about JavaScript and C-style code. First and foremost, if you are unfamiliar or think you are familiar, but really aren’t, (because of say, the welcoming C-style syntax) with JavaScript consider reading JavaScript: The Good Parts. It is a quick, light read that provides invaluable insight into the language. JS is an immensely expressive language but some of the baggage surrounding it can make it seem like little more than a toy language that is intolerable to code in and should generally be avoided at all costs. This simply isn’t true, so go on, have a read.

Update: Indentation and brace style should follow the 1TBS/OTBS style.

When programming in JS always keep in mind that JS doesn’t come with a linker, so everything is put in the global namespace unless you explicitly state not to. All program variables should be kept either within a lambda scope e.g.

(function () {
var a = ‘foo’,
     b = function () {},
     c = ‘bar’, …
)());

or within a namespace e.g.

MYPROGRAM = window.MYPROGRAM || {};
MYPROGRAM.a = ‘foo’;
MYPROGRAM.b = function b () {};
MYPROGRAM.c = ‘bar’;

-or-

MYPROGRAM = window.MYPROGRAM || {
    a: ‘foo’,
    b: function b () {},
    c: ‘bar’
};

Also, all variables should be declared with the var keyword, otherwise the JS interpreter will automatically link it to the global object. Further, unlike many other C-style languages JS does not have block scope (neither does PHP for that matter). In languages with block scope it is suggested to not declare variables until they are used. Since both PHP and JS only have functional scope, variables should be declared at the top of the function in which they are used rather than right before they are used.

Since JS provides no linkeranything can overwrite anything else, and no runtime warning or errors are provided when such an event occurs. A lot of code in JS seems to be copied and pasted from elsewhere and this code could very well have strange interactions with your programs if the original author forgot to properly declare a variable or named one of his functions the same as yours. Always encapsulate your program variables and keep them out of the global namespace. When using a namespace, keep the namespace as all caps, this is to differentiate it from other methods or functions.

Generally, when naming function or methods, the name should start with a lower case letter, unless it is a constructor function. If it is a constructor function it should start with an upper case letter. e.g.

function Foo (bar) {
   this.hello = function () {
       alert(bar);
   }
}

var myFoo = new Foo("bar");
myFoo.hello(); //"bar"

Since we are primarily dealing with web languages it is best not to mix up code style between different C-style languages such as JS and PHP, as during the course of a day you will most likely be working in many languages. In either language either of the following are acceptable:

if (foo) {
   bar;
}

if (foo)
{
   bar;
}

if (foo)
   bar;

However, the first is the preferred way of writing it. This is due to a limitation in JS. JS implicitly inserts end of statement characters on new lines. So something like:

return
{
   foo: ‘bar’
};

may look like it is returning a new object with the property foo with the value ‘bar.’ However, it is actually returning undefined and the object literal statement is never reached. What is really meant is:

return {
    foo: ‘bar’
};

Moreover, it is very easy to make a mistake such as:

if (foo)
   bar();
   baz();

which was meant to mean:

if (foo) {
   bar();
   baz();
}

but really means:

if (foo) {
   bar();
}
baz();

It is preferred that when declaring arrays or objects that their literal forms are used. So instead of:

a = new Object();
b = new Array();

write:

a = {};
b = [];

The use of whitespace is paramount when writing code. It increases legibility significantly. The suggested use of whitespace is as follows:

  • Use a space before and after any operator e.g. /, +, *, -, &&, etc.
  • Use a space after a function declaration and the beginning and end of the argument definition e.g. function foo (a, b, c) { }. This helps differentiate between a function definition and a function invocation.
  • Use a space after semi-colons

When testing for values in JS always use === or !== operators. Their == and != counterparts do not do what you might think they do and should generally be avoided.

If a function requires many arguments consider putting the arguments in an options object and passing the object into the function instead of having a long list of arguments which may or may not be optional. This alleviates the need to have to remember the order of all the arguments and allows for more verbose code, too. For example:

function foo (bar, baz, qoz, …) {};
foo(1, 2, 3);

can be rewritten as

function foo (args) {};
foo({
   bar: 1,
   qoz: 2,
   baz: 3
});

More to come later…

Code Style is not Subjective p.1

Monday, August 10th, 2009

Over the course of my, so-far brief, career in software development I have seen a plethora of different coding styles fully blanketing the spectrum from awful to outstanding. Software is not a write-only medium and is read (both by humans and machines alike) many more times than it is inked (or typed, punched, plugged, etc). As such, unless participating in some sort of obfuscated code competition, all code should be written with readability in mind. Over the course of the next few days I will attempt to define basic coding styles for C-style languages (like PHP, Java, C#, and JavaScript), SGML style languages (like XHTML, HTML, ColdFusion, and XML), and SQL as well as some best practices.

To begin, I will start with some basic principles that are applicable to the programming field in general.

First and foremost, in order to attain a higher degree of readability the DRY principle should always be kept in mind. Further, code should generally be as verbose as possible. Verbosity allows writing of self documenting code and keeps comments (which aren’t necessarily updated as often as the code is, which is a bad thing indeed) to a minimum. It is very easy to write terse code using quasi-cryptic operators such as ++ or — and short variable names such as r instead of row. However, while this practice could be considered elegant it can often be difficult to read and can be subject to bug introduction during maintenance (or even development).

Update: After some input from co-workers I thought it best to amend the statement discouraging the use of terse operators like ++ and –. While I personally feel it best to not use them, I can also understand that to some, it is much more readable to see i++/i– over i = i + 1 and i = i -1. If you are unfamiliar with all aspects of these operators than you should avoid using them and instead use the more verbose notation. However, if you choose to use these operators, it may be in your best interest to leave a comment or two about what these operators do if your code is to be maintained by persons without in-depth knowledge of them.

For example, consider the following piece of JS that determines if the two given arrays are equivalent:

(function (a1,a2){   
    for (var i = 0, var j = 0; i < a1.length && j < a2.length; i++, j++)
       if (a1[i] !== a2[j]) return false;
    return a1[i] === a2[j];
}([1,2,3],[1,2])); //false

This code is a bit cryptic to read and is generally poorly written for a number of reasons, some of which won’t be discussed until the JS portion of this series. It would be much easier to read and maintain if it were written as:

function arrayCompare (array1, array2) {
   var length1 = array1.length,
        length2 = array2.length,
        i = 0, j = 0;
   while (i < length1 && j < length2) {
      if (array1[i] !== array2[j]) {
         return false;
      }
      i = i + 1;
      j = j + 1;
   }
   return array1[i] === array2[j];
}
arrayCompare([1, 2, 3], [1, 2]); //false

The code is much more verbose and it is clear what each variable represents. Since there using a for loop here would make the code much more terse using a while loop makes it much more readable. It is also easy to see that both i and j are incremented once through each iteration of the loop and it is clear what the body of the while and if statements are.

All code should adhere to strict indentation and other white-space schemas. Lines of code should also fit in your IDE (or text) editor’s window without the need for scrolling or window resizing. Unfortunately, this is a little bit subjective as not all monitors are created equally and the size of your window may differ from the size of your fellow developers window, or perhaps the size of your window on another machine. Therefore, I recommend adhering to 79 characters max per line. This will almost guarantee the code will display inside of the editor window without wrapping or the need for scrolling or resizing. 79 characters is the default max of dumb terminal windows.

Such a small character limit may seem like a large restriction, but for comparison, my Eclipse IDE with the navigator and outline windows in view leaves only 101 characters for the text editor at a 1600×1200 resolution. It is possible to be very expressive within 79 characters and anything beyond that should go to a new line and be indented appropriately.

I think that’s a good start. More to come later.

One Bug to Rule Them All

Friday, July 17th, 2009

Just read an interesting article about a buffer overflow bug dealing with the select method. Interestingly, the proof of concept choose the max signed 32-bit integer of 2147483647 to cause the bug. This does indeed crash IE8, but does not crash FF3.5. I didn’t personally test any other browser. What’s even more interesting about this though is that this shouldn’t overflow in JS, as all numbers in JS are internally represented as 64-bit floats. Moreover, even if JS had a 32-bit integer number that number shouldn’t overflow at all. Given that the bug deals with the select element, my suspicion is that it is a DOM related issue that is being misrepresented as an ECMA script related issue.

The danger of using frameworks that augment default objects

Monday, July 6th, 2009

After a bit of thought it occurred to me just how dangerous using frameworks, like Prototype, which augment built-in objects in large-scale production projects.  As many avid JS developers are most likely aware, the prototype inheritance model of the language allows for the extension of virtually any object in the language, user defined or otherwise. However, this can also have severe side effects if a browser developer decides to include a method of the same name as your framework of choice added to a built-in object some years back.

Take, for example, the venerable each function. Prototype extends enumerable objects such as arrays with this function in its prototype. e.g.

Array.prototype.each = function(f){

   var i = 0;

   for(; i < this.length; i = i + 1){

      f(this[i],i);

   }

}

Now imagine this code is called in thousands of places in your project. Let’s also assume before extending the object prototype such a library would also check for the existence of said function as to not overwrite default behavior e.g.:

if(!Array.each) {

   …

}

Now imagine an A-grade browser’s newest version now includes the each method on collection objects by default but with either an argument omitted, or the argument order swapped. All of the code that you had written that depended on the augmented each function instead of the built-in each function will now no longer work in that browser, and you can’t very well force users to not use the latest version of IE, Safari, Firefox, or Opera because a decision was made some years back to use a framework which augments built-in objects.

This decision may now cost many thousands of dollars, if not more. Other JS frameworks such as YUI or jQuery seem like a much more sound choice in this respect. In my opinion, YUI is probably the better of the two choices as it is far easier to pick-up and master by developers of other, not necessarily web-centric languages. Yet in the scope of this rant, both are equally more sound than using something which could very easily and most likely inevitably break a few years down the road, costing many unnecessary man-hours, and perhaps dollars, to fix.

Radial Breakout Woes

Sunday, March 15th, 2009

For a project in CS U540 I had to create an interactive 2D program that supported a few linear transformation (e.g. scaling, rotation, reflection, etc.). I decided to make a break out type game. However, I didn’t want to create a normal break out clone as that seemed a little boring. In hindsight I should have. Getting the physics right on this game has proven to be quite a challenge. Moreover, I shouldn’t have chosen Java as the language to write it in. I still can’t figure out a way to keep the mouse pointer inside the game window while the game has focus and keeping it cross platform. Below is a screenshot of the game in an early stage:

Radial Breakout Screenshot

Radial Breakout Screenshot

Basically I now have several issues:

  • The game is a CPU hog. Coding it to use Windows messages will limit it to running on Windows only. Using a Java timer for updates will mess around with the fluidity and essentially keep it being a CPU hog, just one that hogs slightly less often.
  • The only way I have found to get the mouse to stay within the game window is to use the Robot class. This unfortunately would fail if I wanted to distribute the game using applets or Java Web Start.
  • The hit detection for the paddles isn’t quite where I want it to be.
  • The ball movement isn’t quite right either. This stems from the rather messed up deflection algorithm I am using:
    • If the ball hits a paddle. Determine the intersection of the ball’s current vector with the tangent line crossing the apex of the paddle. Determine the angle between the normal of the ball’s velocity vector and the intersection. Rotate the ball’s movement vector by this angle. (Currently this is not only slow, but buggy)
    • If the ball hits a paddle and after rotation it is still going out of bounds reflect the ball back in bounds. (This should never have to be done.
    • If the ball hits a brick swap the movement vectors. Essentially reflecting the ball 90 degrees.
  • The ball ends up moving so fast that it becomes very difficult to play.
  • If I stuck with my original intention of having the bricks damage, stun, or anything else detrimental to the paddles the game wouldn’t be very fun or playable.

So I’m not really sure what I’m going to do now. I’m thinking of scrapping the project all together as far as its Java implementation goes and starting it back up in C#/XNA. I think having two joysticks to control the paddle(s) rather than a mouse and key modifiers will feel much more natural. Plus it will give me some nice experience with XNA.

For those interested. The Functional Specification and Game Files for the first build of Radial Breakout are available. The jar inside the game files archive is executable and all source code as well as a manual is there as well.

Internet Explorer, Tables, the DOM, and what not to do…

Saturday, March 14th, 2009

A post on reddit a few weeks ago finally inticed me enough to sign up for an account on the popular social link sharing site. The post was at a blog by Eric Vasilik (who supposedly had something to do with making IE a horrible platform to develop for) that dealt with having to insert the new rows into an existing table. Intuitively, most coders would just use the .innerHTML property and go about their day until they later test their code in IE and then everything explodes because IE doesn’t play nice when it comes to tables and .innerHTML. Especially when inserting new rows. What really irked me about the whole thing though was the way Mr. Vasilik was going about solving his problem.

For reasons beyond me Mr. Vasilik decided that it would be best to not just use the DOM methods and insert rows in that manner. Instead Eric wrapped his newly created HTML for table rows inside of <table> and <tbody> tags, put them into a hidden element on the page, and then looped over rows in the currently visible table calling the replaceChild method on the table in order to swap out the rows. This solution does, in fact swap out all of the rows. Unfortunately it is painfully slow.  Having to use the DOM alone for this is already significantly slower than using innerHTML (but IE makes this impossible, at least for versions 7 and lower) but using both innerHTML and the DOM is even slower than that. Instead of creating the table rows as strings Mr. Vasilik should have JSON encoded the tables so that he would’ve had them as objects after a quick eval or if he had to create them in the code then he should’ve used the DOM to create the objects and used insertRow, insertCell, etc. on the table object instead of using the least efficient approach he could think of.

I ended up writing a few speed tests to test innerHTML, DOM, DOM/appendChild, and DOM/innerHTML/replaceChild which can be found here (needs Firebug to run).  Not surprisingly, using innerHTML to create a new table, then calling replaceChild to swap table rows was the slowest of the tests. Full code below:

(function()

{

var table = document.getElementById(‘test’);

var tr, td;
<pre id="line1">  console.time("DOM Manipulation");
  for(var i = 0; i &lt; 1000; i++)
  {
      tr = table.insertRow(-1);
      td = tr.insertCell(-1);
      td.innerHTML = "Test";
  }
  console.timeEnd("DOM Manipulation");
  table.innerHTML = "";
  console.time("innerHTML");
  var rows = "";
  for(var i = 0; i &lt; 1000; i++)
  {
     rows += "&lt;tr&gt;&lt;td&gt;test&lt;/td&gt;&lt;/tr&gt;";
  }
  table.innerHTML = rows;
  console.timeEnd("innerHTML");
  table.innerHTML = "";
  console.time("DOM, appendChild");
  for(var i = 0; i &lt; 1000; i++)
  {
    tr = document.createElement("TR");
    td = document.createElement("TD");
    td.innerHTML = "Test";
    tr.appendChild(td);
    table.appendChild(tr);
  }

  console.timeEnd("DOM, appendChild");
  rows = "&lt;table id=’test2′&gt;";
  console.time("DOM, span, replaceChild");
  for(var i = 0; i &lt; 1000; i++)
  {
    rows += "&lt;tr&gt;&lt;td&gt;test DOM, span, replaceChild&lt;/td&gt;&lt;/tr&gt;";
  }
  rows += "&lt;/table&gt;";

  var spanTemp = document.getElementById("temp");
  spanTemp.innerHTML = rows;
  var varTempTableRows = spanTemp.getElementsByTagName("table")[0].getElementsByTagName("TR");
  for(var i = 0; i &lt; varTempTableRows.length; i++)
  {
     table.replaceChild(varTempTableRows[i], table.childNodes[i]);
  }
  console.timeEnd("DOM, span, replaceChild");
})();

UPDATE: Seems like this completely changed in FF3.5 and using innerHTML is significantly faster than any of the previous methods.