How would jQuery look like in… Java?

I was thinking about how jQuery would look like when implemented in other languages: Is it possible to offer the same API jQuery currently provides in JavaScript? Is it easier, or more difficult? What are the differences?

Since I’m most familiar with Java, I started to mock up some classes. I’m relying on some of Java 5’s features to be able to write some nice code. Wouldn’t want to try that with 1.4.

Let’s get started with some code that actually uses jQuery

import static com.jquery.jQuery.jQuery;

import com.jquery.Element;
import com.jquery.Event;
import com.jquery.EventListener;
import com.jquery.ReadyListener;
import com.jquery.jQuery;

public class Application {
  
  public void main() {
    jQuery(new ReadyListener() {
      public void handle(Element document) {
        jQuery("input:checked").val("newvalue").val();
        
        // need to split declaration and call to click, otherwise
        // we'd get "The local variable anchors may not have been initialized"
        final jQuery anchors = jQuery("a");
        anchors.click(new EventListener() {
          public void handle(Event e) {
            e.preventDefault();
            jQuery(e.target).val("foo");
            anchors.val("bar");
          }
        });
      }
    });
  }

}

Here we have the first use of one Java 5 feature: Static imports. We’re importing the static jQuery method from the jQuery class:
import static com.jquery.jQuery.jQuery;

Of course the compiler complains that we are declaring a class method that has the same name as a constructor, but as it is no error, we don't have to worry about that.

Next we import some classes and interfaces to work with, all very simple:

public interface ReadyListener {
	public void handle(Element document);
}
public interface Function {
	public void apply(Element element); 
}
public interface Element {
	public String getAttribute(String name);
	public void setAttribute(String name, String value);
}
public interface EventListener {
	public void handle(Event e);
}

Event isn't an interface, but an abstract class with a some public final properties. The example has only target and source, other properties like keyCode would be there too.

public abstract class Event {

	public final Element target;
	public final Element source;
	
	protected Event(Element source, Element target) {
		this.source = source;
		this.target = target;
	}
	
	public abstract void preventDefault();
	public abstract void stopPropagation();
	
}

Now for anyone saying, "but don't ever make fields public, you can't change the implementation then!", take a look at the SWT API, it uses very similar event objects.

Ok, back to our initial example:

public void main() {
  jQuery(new ReadyListener() {
    public void handle(Element document) {
      jQuery("input:checked").val("newvalue").val();
      
      // need to split declaration and call to click, otherwise
      // we'd get "The local variable anchors may not have been initialized"
      final jQuery anchors = jQuery("a");
      anchors.click(new EventListener() {
        public void handle(Event e) {
          e.preventDefault();
          jQuery(e.target).val("foo");
          anchors.val("bar");
        }
      });
    }
  });
}

This fake main method would be somehow hooked into the browser's API, but that isn't the focus here.

Anyone who has worked with jQuery should recognize everything here. Of course the code for defining anonymous functions is a bit more noisy then JavaScript's plain function() {}, but the purpose of the ReadyListener is clear and can't be confused with a normal EventListener or a Function.

The actual code to select elements and do something with them is extremley close to jQuery itself, thanks to JavaScript having a syntax close to Java: jQuery("input:checked").val("newvalue").val();

It is also possible to create closures, though it is a bit more restricted by the compiler, and therefore less error prone. Or more error prone, due to more code. Decide for yourself:

final jQuery anchors = jQuery("a");
anchors.click(new EventListener() {
	public void handle(Event e) {
		e.preventDefault();
		jQuery(e.target).val("foo");
		anchors.val("bar");
	}
});

We need to split the assignment to the final variable "anchors" before we call click, otherwise the compiler complains with "The local variable anchors may not have been initialized". Makes sense to me. I wonder if the problem that is prevented here, executing the handler before the click method returns, can occur in JavaScript?

Ok, now to the actualy jQuery class:

package com.jquery;

import static com.jquery.Window.document;

public class jQuery {

  // use an array instead of a list to guarantee non-destructive behaviour
  private final Element[] elements;

  // private constructors
  private jQuery(Element element) { elements = new Element[] { element }; }
  private jQuery(Element[] elements) { this.elements = elements; }
  
  // factories
  public static jQuery jQuery(String expression) {
    return new jQuery(document).find(expression);
  }
  public static jQuery jQuery(Element element) {
    return new jQuery(element);
  }
  public static jQuery jQuery(Element[] elements) {
    return new jQuery(elements);
  }
  public static jQuery jQuery(String expression, Element context) {
    return new jQuery(context).find(expression);
  }
  public static void jQuery(ReadyListener listener) {
    new jQuery(document).ready(listener);
  }

  // DOM ready event, manages ready list
  public void ready(ReadyListener listener) {}
  
  // provide access to all or a single element
  public Element[] get() { return elements; }
  public Element get(int index) { return elements[index]; }

  // find elements and return new jQuery object
  public jQuery find(String expression) { return jQuery(new Element[] {}); }
  
  // iterate through elements and apply a function
  public jQuery each(Function handler) {
    for (Element current : elements) {
      handler.apply(current);
    }
    return this;
  }
  
  // get value of first element
  public String val() { return elements[0].getAttribute("value"); }
  
  // set value of all elements
  public jQuery val(final String value) {
    // use each to iterate
    // actually a for loop would be more efficient here, less code
    return this.each(new Function() {
      public void apply(Element element) {
        element.setAttribute("value", value);
      }
    });
  }

  // manages click event handlers
  public jQuery click(EventListener listener) { return this; }

}

I think implementations of each() and val() are worth some more attention. While we don't win much when using each() internally, it is possible to provide almost the same API as the JavaScript implementation of jQuery. We just need a bit more effort to create the Function object, but a good IDE (Eclipse is my favorite) can help alot. I don't have to type more then "new Function()" and press ctrl+enter inside the parentheses.

I'd find it quite interesting to see how similar implementations, or rather APIs, would look like in other languages, be it PHP, Python, PERL, ColdFusion or Ruby.

Ruby is especially intersting for me. When looking at the progress of JRuby, it seems like we can get to start using Ruby on the JVM this year. So far the performance isn't outstanding because all code is interpreted, but once they start working on the compiler, compiling Ruby code into bytecode, the performance should improve a lot.

I hope to get some more examples of jQuery in other languages, and will post updates accordingly.

-Jörn

No more comments.
  1. Very interesting concept, not bad idea and nice work, however what will be the range to use that? jsps? jars?

    Good job!!

    Cya

  2. Hi Klederson,

    view layers should be where this could be applied. Think of a replacement for XSLT to generate HTML or XML.

    In the meantime I’ve experimented with jQuery on the serverside, run via Rhino. That may get more interesting once a Rhino implementation of ECMAScript 4 is available…

  3. Hi, I have in fact written a web framework based on jQuery. It is still under development. You can download a version from sourceforge.

    I am currently looking for contributors, and I am actually the only one working on it. I would be very grateful if you could check out the codes, and contact me if you are interested in contributing.
    http://sourceforge.net/projects/castafiore/