The Artima Developer Community
Sponsored Link

Software and Return on Investment
Netbeans JMI programming...
by Gregg Wonderly
September 12, 2006
Summary
The Java Metadata Interface available in the Netbeans 5.0 and later platform is powerful. But, the API could be simpler. I've been putting together a helper class.

Advertisement

Basic JMI programming

The JMI programming model has some very repetative API designs which make it easy to remember what to code. However, there are so many classes and multi-object traversals needed, that I felt that some brevity was possible and that there would not be a loss of information in doing that.

Below are some examples of what I've been doing to change how I write JMI code.

Defining a Method with JMI

    JavaModelPackage jmp = ...;

    Method method = jmp.getMethod().createMethod();
    method.setName( name );
    int mods = Modifier.;
    method.setModifiers( mods );
    method.setType( retType );
This pattern, of course is repeated at every place that you need to define a Method object. In addition, you might define Javadoc text and/or the text of the body of the method.
    method.setJavadocText("This method is used to...\n" );
    method.setBodyText("int i = 0;\n"+
        "while( i < 20 ) {\n"+
        "\tif( ... ) ++i;\n"+
        "}\n" );
There is of course a couple of other things about method definition that are needed, Parameter definitions, and perhaps thrown exceptions. In JMI, these things are covered in the API using the following kinds of code.
    Parameter p1 = jmp.getParameter().createParameter();
    p1.setName("...");
    p1.setType( ... );
    p1.setModifiers( ... );
    method.getParameters().add( p1 );

    JavaClass cls = ...the class the method belongs to...
    String excls = "java.io.IOException";  // for example
    Type ex1Type = (Type)JavaModelUtil.resolveImportsForClass( cls, excls );
    method.getExceptionNames().add( ex1Type );
In this above code, I've used a couple of other parts of JMI. The JavaClass class is the object that describes a particular class. In Netbeans, a module action gets an object that is being acted on, and there are API mechanisms for getting to the JavaClass object from a number of directions. One is the ClassMember.getDeclaringClass() method, if you already have some part of the JMI hierarchy. But, more generally, you just have a netbeans DataObject which requires something like the following, to get a JavaClass.
    Resource resource = JavaModel.getResource ( dob.getPrimaryFile () );
    JavaClass cls = (JavaClass) resource.getClassifiers().get(0);
But, I don't really want to talk about the Netbeans module API now, let's get back to JMI!

The other additional API that is included above is the use of the JavaModelUtil.resolveInportsForClass() method. This method is a convenience method that does a couple of things for you. One, is that it will resolve a type name specified as a string to a Type object. Then, it will augment the passed JavaClass object with an appropriate import of the associated class!

Finally, we might want to actually add this method definition to a JavaClass so that the method will appear in either an interface definition, or a class definition. Simply use cls.getFeatures().add( method ); to add the method.

Defining a Field with JMI

Field definition is largely similar to the definition of a method. Look at the following code example to see where the differences are.
    JavaModelPackage jmp = ...;

    Field field = jmp.getField().createField();
    field.setName( name );
    int mods = Modifier.;
    field.setModifiers( mods );
    field.setType( retType );
After you define the method, you can added it to a class in the same way as a method using cls.getFeatures().add( field ); to do that.

Pushing all of this into an API

The use of a consolidating API, can make it simpler to do the more common, repetative tasks. I've been using static methods in a class named JMI, instead of the above. Here's an example of the simplest case where you have a simple method definition without parameters.
    Method method = JMI.newMethod( jmp, cls, "name", Modifer.PUBLIC, "void" );
This brings all of the simple definition pieces into a single line of code. From this point, you can call various methods on the method object to add parameters, exceptions etc.

But, I've also provided some addition overrides of the method with additional arguments. The following signatures of newMethod() are provided.

/**
 *  The most basic call to just specify the initial information.  This can be used
 *  to get a basic Method to fill in the blanks with more programatic control.
 */
newMethod( JavaModelPackage jmp, JavaClass cls, String name, int mods );

/**
 *  This method adds the return type specification as a String for the most
 *  simple cases where native types or void is the appropriate type.
 */
newMethod( JavaModelPackage jmp, JavaClass cls, String name, int mods, String type );

/**
 *  If the method throws exceptions, this signature lets you specify those.
 */
newMethod( JavaModelPackage jmp, JavaClass cls, String name, int mods, String type,
    String[] exceptions );

/**
 *  This signature adds an array of imports to perform as well.  If there are types
 *  in the method body, or otherwise needing import, you can add those here.
 */
newMethod( JavaModelPackage jmp, JavaClass cls, String name, int mods, String type,
    String[] exceptions, String[]imports );

/**
 *  This signature includes the specification of method parameters.  It uses a
 *  helper class, ParameterInfo, to contain the parameter definitions.
 */
newMethod( JavaModelPackage jmp, JavaClass cls, String name, int mods, String type,
    String[] exceptions, String[]imports, ParameterInfo[] params );

/**
 *  The signatures which include a Type type specification instead of a String type
 *  are the main methods.  The String versions use jmp.getType().resolve()
 *  class and invoke these methods.
 */
newMethod( JavaModelPackage jmp, JavaClass cls, String name, int mods, Type type );

newMethod( JavaModelPackage jmp, JavaClass cls, String name, int mods, Type type,
    String[] exceptions );

newMethod( JavaModelPackage jmp, JavaClass cls, String name, int mods, Type type,
    String[] exceptions, String[]imports );

/** 
 *  All of the work is done in this method.  All the above methods just forward
 *  calls to this method providing null values for the remaining
 *  parameters.
 */
newMethod( JavaModelPackage jmp, JavaClass cls, String name, int mods, Type type,
    String[] exceptions, String[]imports, ParameterInfo[] params );
As you can see, there are a few variations on the basic newMethod signature. In a Netbeans module, you'd then see code such as the following.
    method = JMI.newMethod( jmp, cls, "usePlatformServer", Modifier.PRIVATE, "boolean" );
    method.setJavadocText("This method provides the plugable place to override how the use\n" +
        "of the platform MBeanServer is selected.  The platform server is actively used\n" +
        "by default in this implementation.");
    method.setBodyText( "return System.getProperty(
        getClass().getPackage().getName()+\".noplatform\") == null;" );
    cls.getFeatures().add( method );
There is some reduction in unimportant, repetative JMI API that, I think, cleans up the code. Another example, is shown next. In this example, the code is adding a "setter" method for a field that is retrieved from a user selecting an action on that field with a Netbeans module defined action that might have been in a context menu that was invoked.
    Field fel = ...defined elsewhere...
    String fname = JMI.fixCase( fel.getName() ); // upcase first letter

    Method method = JMI.newMethod( jmp, newIfc, "set"+fname,
        Modifier.PUBLIC, "void",
        null,  // No exceptions
	null,  // No extra imports
        new ParameterInfo[] {
	    new ParameterInfo( "val", fel.getType().getName() )
	}
    );

    method.setJavadocText( "This method is used to set the current value of\n" +
        "the "+name+" field.  The associated MBean attribute will\n" +
        "be capitalized as "+fname+" due to the" +
        "mechanism implemented by the {@link javax.management.StandardMBean}.\n" +
	"@see #get"+fname+"()" );

    cls.getFeatures().add( method );
The Field methods on my JMI class are very similar in nature to the Method ones. If you haven't played with JMI on Netbeans yet, you should set down and give it a whirl. There are a lot of examples on the web. In particular, Geertjan's blog has a large number of examples about doing different things with Netbeans as a user and as a developer of modules.

Talk Back!

Have an opinion? Be the first to post a comment about this weblog entry.

RSS Feed

If you'd like to be notified whenever Gregg Wonderly adds a new entry to his weblog, subscribe to his RSS feed.

About the Blogger

Gregg Wonderly graduated from Oklahoma State University in 1988 with an MS in COMSCI. His areas of concentration include Operating Systems and Languages. His first job was at the AT&T Bell Labs facilities in Naperville IL working on software retrofit for the 5ESS switch. He designed a procedure control language in the era of the development of Java with similar motivations that the Oak and then Java language development was driven by. Language design is still at the top of his list, but his focus tends to be on application languges layered on top of programming languages such as Java. Some just consider this API design, but there really is more to it! Gregg now works for Cyte Technologies Inc., where he does software engineering and design related to distributed systems in highly available environments.

This weblog entry is Copyright © 2006 Gregg Wonderly. All rights reserved.

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us