Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

The first category allows you to treat DOM objects as arrays and maps so that you can use them in conjunction with the Groovy path expression language and treat them like JavaBeans. Here is an example from the tests of using the DOMCategory:

Code Block
borderStylesolid
titleDOMTest.groovy
borderStylesolid
import groovy.xml.*;

def html = DOMBuilder.newInstance().html {
  head {
    title (class:"mytitle", "Test")
  }
  body {
    p (class:"mystyle", "This is a test.")
  }
}

use (groovy.xml.dom.DOMCategory.class) {
  assert html.head.title.textContent == "Test";
  assert html.body.p.textContent == "This is a test.";
  assert html.find { it.tagName == "body" }.tagName == "body";
  assert html.getElementsByTagName("*").findAll {
    if (it["@class"] != "") { return it } 
  }.size() == 2;
}

try {
  html.head;
} catch(MissingPropertyException mpe) {
  println "Categories wear off";
}

As you can see here we are treating DOM objects just as if they were JavaBeans and are accessing them with GPath. The ServletCategory is similarly used when we want to treat the attributes of Servlet API objects as if they were properties since they don't follow the typical conventions for JavaBeans or Maps either. In the GroovyServlet that lets you use scripts as servlets we call GroovyCategorySupport from Java in order to make it possible to use property accessors against the request:

Code Block
borderStylesolid
titleGroovyServlet.java
borderStylesolid
Closure closure = new Closure(gse) {
   public Object call() {
       try {
           return ((GroovyScriptEngine) getDelegate()).run(scriptUri, binding);
       } catch (ResourceException e) {
           throw new RuntimeException(e);
       } catch (ScriptException e) {
           throw new RuntimeException(e);
       }
   }
};
GroovyCategorySupport.use(ServletCategory.class, closure);

...

In order to create your own Categories and extend classes yourself you'll need to understand what the "use" keyword expects to be defined within the class you pass to it. To add a method to a class T, simply define a new class with a static method that whose first parameter is of type T. Here is a simple example from the tests:

Code Block
borderStylesolid
titleCategoryTest.groovy
borderStylesolid
class StringCategory {
    static String lower(String string) {
        return string.toLowerCase();
    }
}

use (StringCategory.class) {
    println "TeSt".lower();
}

...

Here is an example of using this as an end user in order to add methods to Apple's own NSDictionary and NSArray class in order to manipulate their Cocoa objects as if they were native Groovy objects:

Code Block
borderStylesolid
titlebookmarks.groovy
borderStylesolid
#!/Users/sam/bin/groovy
// Put /System/Library/Java in your CLASSPATH
import groovy.xml.*;
import groovy.xml.dom.*;
import java.io.*;
import com.apple.cocoa.foundation.*;

class PropertyListCategory {
        static Object get(NSDictionary dictionary, String key) {
                return dictionary.objectForKey(key);
        }
        static Object getAt(NSArray array, int i) {
                return array.objectAtIndex(i);
        }
        static void each(NSArray array, Closure closure) {
                for (i in 0..array.count()-1) {
                        closure.call(array[i]);
                }
        }
}

filename = "${System.getProperty("user.home")}/Library/Safari/Bookmarks.plist";
data = new NSData(new File(filename));
errorString = new String[1];
format = new int[1];
plist = NSPropertyListSerialization.propertyListFromData(data,
            NSPropertyListSerialization.PropertyListImmutable, format, errorString);

if (errorString[0]) {
        println "Error: ${errorString[0]}";
        System.exit(1);
}

def getURLs(NSArray array, list) {
        array.each {
                getURLs(it, list);
        }
}

def getURLs(NSDictionary dict, list) {
        if (dict.Children != null) getURLs(dict.Children, list);
        if (dict.URIDictionary != null) {
                list.add([title:dict.URIDictionary.title, url:dict.URLString]);
        }
}

def getURLs(NSDictionary dict) {
        use (PropertyListCategory.class) {
                def list = [];
                getURLs(dict, list);
        }
        return list;
}

println getURLs(plist);

...