Zen and the art of KVC

Key Value Coding – it’s one of the reasons WebObjects is so much fun.

The Basics

For those uninitiated, KVC allows your code to access method, attributes, or variables in your classes using string keys. When you are binding to a WOElement like WOString in WebObjects Builder you are using KVC (WOString, in-case you didn’t know, is a "String" with "WO"… in-joke, sorry). But KVC can help you a lot in your own code.

For instance, this:

public String myFirstName() {
	return person.firstName();
}

can be re-written as this:

Person person; // assume exists and implements NSKeyValueCoding
  
public String myFirstName() {
	return (String)person.valueForKey("firstName");
}

You can also set the value on an object using:

public String setMyFirstName(String newName) {
	person.takeValueForKey(newName, "firstName");
}

Why bother? Well for one thing, when using KVC you don’t need to know the type of the object. So this code would work just as well:

EOEnterpriseObject eo; // assume exists
  
public String myFirstName() {
	return (String)eo.valueForKey("firstName");
}

Meaning the eo could be a Person, or an Employee, or a Customer, or any other object that returns a value for firstName. This becomes very useful when you are writing reusable objects, handling conditional navigation in your components (ie: when the destination component will not be determined until run-time), or creating bindings for reusable components.

How does WebObjects find a match for your key? The KVC implementation in WebObjects will match using these rules:

  • When "getting" values:
    1. a method named firstName or getFirstName
    2. a variable named firstName or _firstName
  • When "setting" values:
    1. a method named setFirstName
    2. a variable named firstName or _firstName

Based on these rules, I like to name my accessors: varName() and their variables: _varName so there is no confusion about which will be hit by KVC.

Following the Path

In addition to valueForKey and takeValueForKey, you can access values through their paths using valueForKeyPath and takeValueForKeyPath. Lets say that you have an order that has an approver who has an address. You could access the address like this:

Order order; // assume exists
  
public String approverAddress() {
	return (Address)order.valueForKeyPath("approver.address");
}

If you were in a WOComponent or some other object that implemented the NSKeyValueCoding interface, you could use code like this:

public String approverAddress() {
	return (Address)this.valueForKeyPath(
			"order.approver.address");
}

This example demonstrates why KVC takes me to my happy place: If any of the objects in the path are optional, KVC will gracefully handle the nulls. So the code above could potentially replace:

public String approverAddress() {
	if (order != null) {
		Person approver = order.approver();
		if (approver != null) {
			return approver.address();
		}
	}
	return null;
}

A Strong Foundation

Key Value Coding is implemented in many of the Foundation classes like NSArray, NSDictionary, etc. As an example, if you have an NSArray called orders that contains order objects that all need to be canceled by setting their cancelFlag to true. You could use:

NSArray orders; // assume exists
  
public String cancelOrders() {
	orders.takeValueForKey(Boolean.TRUE, "cancelFlag");
}

Couple this with valueForKeyPath and takeValueForKeyPath you have a very powerful set of additional tools at your disposal.

Putting it all together

Here is a final example using KVC and the NSArray operators from ProjectWONDER that I mentioned previously here.

Perhaps we have this:

order <-->> product <-->> sku

We want to get all of the skus. Each order will have several products but not all products will have skus. Using traditional java we’d end up having to do something like this:

public NSArray allSkus() {
	NSArray products = order.products();
	int productCount = products.count();
	NSMutableArray allSkus = new NSMutableArray();
	for (int i = 0; i < productCount; i ++) {
		Product product = products.objectAtIndex(i);
		NSArray theseSkus = product.skus();
		if (theseSkus != null && theseSkus.count() > 0) {
			allSkus.addObjectsFromArray(theseSkus);
		}
	}
	return allSKus;
}

Using KVC and the @flatten opperator from ProjectWONDER we can do something like this:

public NSArray allSkus() {
	return (NSArray)order.valueForKeyPath(
				"products.skus.@flatten");
}

First we ask the order for its products, then we ask the products for their skus. KVC will discard any null results, and we end up with an NSArray of NSArrays of skus. The final @flatten operator will flatten this nested array into a single NSArray of skus.

Want the result sorted by skuNumber? No problem:

public NSArray allSkus() {
	NSArray allSkus = (NSArray)order.valueForKeyPath(
				"products.skus.@flatten");
	return (NSArray) allSkus.valueForKeyPath(
				"@sortAsc.skuNumber");
}

Embracing Key Value Coding can reduce the code you write and make your code more robust. It can also help you work the "WebObjects Way".

I highly recommend it.

Note: If you feel uncomfortable implementing all of ProjectWONDER just to get the NSArray operators, don’t. Just add the ERXArrayUtilties.java file to your project and call ERXArrayUtilties.initialize(); in your Application’s constructor.

Programatically creating a WODisplayGroup (take 2)

I wrote about programatically creating WODisplayGroups here. In that example I use a find() method to explicitly do a fetch, and those objects were assigned to the the WODisplayGroup.objectArray().

That is just my preferred way of handling the object fetch, but of course it is not the only way. If you wanted to emulate what happens if you create a WODisplayGroup by dragging an Entity from EOModeler into WebObjects Builder, you might do something like this:

protected WODisplayGroup _displayGroup;
protected EOEditingContext ec;   //Assume exists
protected String dateSourceName; //Name of the Entity
 
public WODisplayGroup displayGroup()
{
    if (_displayGroup == null) {
        _displayGroup = new WODisplayGroup();
        _displayGroup.setNumberOfObjectsPerBatch(batchSize);
        EODatabaseDataSource ds = new EODatabaseDataSource(
					ec, 
					dataSourceName);
        _displayGroup.setDataSource(ds);
        _displayGroup.fetch();
    }
    return _displayGroup;
}

You could also create a WODisplayGroup based on a master detail relationship like this:

protected WODisplayGroup _displayGroup;
protected EOEnterpriseObject masterObject; //The Master object
protected String detailKey; //The name of the relationship 
                            //in the masterObject that will
                            //provide the _displayGroup's objects
 
public WODisplayGroup displayGroup()
{
    if (_displayGroup == null) {
        _displayGroup = new WODisplayGroup();
        _displayGroup.setNumberOfObjectsPerBatch(batchSize);
        EODetailDataSource dds = new EODetailDataSource(
			masterObject.classDescription(),
			detailKey);
        _displayGroup.setDataSource(dds);
        _displayGroup.setMasterObject(masterObject);
        _displayGroup.setDetailKey(detailKey);
        _displayGroup.fetch();
    }
    return _displayGroup;
}

As before the WODisplayGroup API is here

Extending EOGenerator templates

The default EOGenerator templates (JavaSourceEOF52.eotemplate and JavaSubclassSourceEOF5.eotemplate) only implement the basic logic in the generated _EO.java and EO.java classes.

I’m assuming you know what eogenerator does, and you understand how the _EO.java and EO.java classes relate to one another. If you don’t, ask, and I’ll try to explain.

For instance, for an entity named Person with the attributes firstName and lastName and a to many relationship to PhoneNumbers named phoneNumbers you’d end up with this:

  • Getter and setter accessor methods for firstName
  • Getter and setter accessor methods for lastName
  • Getter and setter methods for phoneNumbers
  • addTo and removeFrom methods for phoneNumbers

This is about the same amount of java that you’d get from EOModeler if it generated your java for you. Acceptable, but we can do much better.

My Template

My templates (which you can download here) are derived from the advanced sample template that ships with eogenerator (EOGJavaSource.eotemplate) and create the following additional methods in your _EO.java class:

For each to-many relationships

  • addToRelationshipNameRelationship(Object obj) – calls addObjectToBothSidesOfRelationshipWithKey on the relationship with obj
  • removeFromRelationshipNameRelationship(Object obj) – calls removeObjectFromBothSidesOfRelationshipWithKey on the relationship with obj
  • createRelationshipNameRelationship() – creates a new object matching the destination type of the relationship, inserts it into this EO’s EOEditingContexts and adds it to the relationship.
  • deleteRelationshipNameRelationship(Object obj) – removes obj from the relationship and then calls editingContext().deleteObject(obj)
  • deleteAllRelationshipNameRelationships – iterates through all of the objects in the relationship, calling deleteRelationshipNameRelationship.

For each model defined fetch specifications

  • objectsForFetchSpecificationName(EOEditingContext ec, AObject aObjectBinding, BObject bObjectBinding, …) – a static method that returns any objects matching the fetch spec FetchSpecificationName. It takes typed objects for any bindings defined in the fetchSpec.

Using the templates

Using my templates is straight forward, simply specify them when you call EOGenerator (see this post for more details). Before you use my templates however, you will need to specify the name of the class that your EO’s should inherit from (EOGenericRecord is the default). Replace the reference to CBEOBaseClass in the constructor section of CBJavaSourceEOF5.eotemplate (it should be around line 29).

Extending the templates

EOGenerator uses a tool called MiscMerge to create the java class from the EOModel. There is a MiscMerge.rtf included in the eogenerator distribution, but you can figure out what is going on pretty easily just by looking at the templates (the template that does most of the work is CBJavaSourceEOF5.eotemplate).

One of the downsides to using Key Value Coding is that you lose compile time checking on valueForKey methods. For instance, calling:

valueForKey("anAtribute")

will compile fine, but fail at runtime if the attribute is actually called anAttribute.

So, one extension you may wish to make to my templates is to have static strings created to that map to each of your attributes. This means that you can call:

valueForKey(MyEO.AN_ATTRIBUTE)

and the compiler will catch your typos.

The EOGJavaSource.eotemplate included with eogenerator includes an example of how to do this. The template code between line 34 and 39 will create a static string variable for each of your attributes and relationships. Simply copy these lines of code (in between the <$comment … $> and <$comment) and paste them below the constructor in CBJavaSourceEOF5.eotemplate.

EOGenerator is a very powerful tool, it will get you a lot of machine generated (and more importantly machine maintained) code that will make your applications simpler and easier to build. I highly recommend exploring its capabilities a little.

Using WebObjects DirectActions – pt. 5

Calling a DirectAction with values from a WORedirect

WORedirect is useful when you need a regular Component action (i.e. non DA) to lead somewhere other than another WOComponent (i.e. If you need to perform some cleanup and return a company homepage when a logout link is clicked).

Luckily for us, a WORedirect can also easily allows us to redirect to a DirectAction, as the following code demonstrates:

String searchString; 		//assume exists
String category;		//assume exists
String pageOfResults = "0"
 
public WORedirect performSearch() {
    NSMutableDictionary dict = new NSMutableDictionary();
    dict.takeValueForKey(searchString, "searchString");
    dict.takeValueForKey(category, "category");
    dict.takeValueForKey(pageOfResults, "page");
    String url = context().directActionURLForActionNamed(
                 "search", dict ); 
    WORedirect redirect = new WORedirect(context());
    redirect.setUrl(url);
    return redirect;
}

This hypothetical action is bound to a WOSubmitButton in a WOForm on a WOComponant. It is important to note that its return type is WORedirect instead of the usual WOComponent.

It function is pretty clear, it creates a new NSMutableDictionary and populates it with the values and keys that we need to append to our DirectAction URL. The single line of code does most of the heavy lifting:

String url = context().directActionURLForActionNamed( 
                  "search", dict );

It creates the DirectAction url (in this case for the searchAction) for us and append the values from our NSMutableDictionary. Finally we set the url for the WORedirect and return it.

The directActionURLForActionNamed comes from WOContext and it’s API can be found here.

Well, I think this will be the last DirecAction specific post. I’ve covered pretty much everything I set out to, so I think I’ll take a break an look for a different topic to examine. If anyone has any suggestions, let me know.

Auto Java Syntax checking in XCode

I don’t use XCode if I can help it (no sir, don’t like it) so I spend most of my time in Eclipse. I find it a superior Java IDE for a number of reasons (build on the fly, code completion, no index, etc) and one of the biggest reasons for me is syntax checking. I find it saves me a ton of time, no need to build and wait for the syntax error before realizing I’ve missed a ";"

Kieren has posted an interesting tip that gives you access to some of this yumminess in XCode. Granted, it’s not as nice as Eclipse, but next time I’m forced to use XCode it’ll come in handy.