HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

Any discussion of the Java reflection classes first has to explain their importance and the types of tasks that can be solved with them. The Java reflection classes can help solve many generic tasks by abstracting out the task from the specific use of the task. The reflection classes provide a dynamic (runtime) way to find out information about a class: its methods, fields, inheritance, interfaces and so on. By understanding the reflection classes, many repetitive tasks can be made generic and quickly applied to a large set of classes. Some examples include automatic creation of classes to create objects from a class name, SQL statements and XML (Extensible Markup Language) generation. This article offers an overview of the reflection classes, a simple analysis and design for generating XML, an explanation of its implementation and, hopefully, some insights into Java programming.

The AutoXML class and the AutoXMLable and XMLable interfaces were written for this article, which also uses the classes Person, FullName, Address and SampleXML to demonstrate the use of the AutoXML class. My ultimate goal is to demonstrate the creation of XML for any class with the following code:

String myXML=AutoXML.toXML(myObject);

Overview of the Reflection Classes
In general, the Reflection classes help describe and manipulate a Class, a Field and a Method, and have supporting classes to describe a class Constructor, an Array and a field or method Modifier. (The class for reflection permissions and an InvocationTargetException are not covered here.)

To navigate through the many classes and methods, just remember the basics of Java: an object is an instance of a class and has a getClass() method. The object returned is of type Class. The name of the class can be obtained by using the getName() method. This is important because the AutoXML tagname in general is the same as the class name (without the package name).

A class has attributes (obtained by getFields), which can then be manipulated. A class has methods that are obtained by getMethods(). A method can take parameters, obtained by getParameterTypes(). A method has a return value, which is obtained using getReturnType(). Finally, class attributes, methods and the method's return object can have modifiers (public, static, primitive, etc.) and can be obtained using getModifiers().

Unlike the other methods, getModifiers() returns an int, and you should call the appropriate static method to determine the specific property of the modifier. For example:

int currentModifier =
currentMethod.getModifiers();
if (Modifier.isPublic(currentModifier)) {System.out.println("Method is public");}

There are many methods in java.lang.reflect for getting an int, a double, etc. The key to reducing the complexity of programs is realizing that the method get() returns an object and that String.valueOf () can handle all of the nine primitive types.

Task Description: XML
XML is a format that describes data and is becoming popular on the Internet. XSL (Extensible Style Language) is used to change the presentation of the XML data. For more information on the specifications and benefits of XML, check out www.w3c.org/.

The XML data is between a "tag" that describes the type of Data. The code in Listing 1 might be used to describe a class called FullName having attributes of FirstName, MiddleName and LastName. Contained objects have their respective XML within the tag of the container. Expanding the above example to describe a Person might look like the code in Listing 2. This fits into an object-oriented framework quite easily.

Each class only needs to know how to generate its own XML piece according to the following procedure (assuming each class has a method called toXML()) in a method called toXML():

  1. Put classname between beginning tag delimiters.
  2. If the attribute is an array, iterate over the entire array for steps 37.
  3. For each attribute, put the attribute name and optional indices within a tag.
  4. If the attribute is a primitive type, append the value of the attribute.
  5. If the attribute is a complex object that is XMLable (implements XMLable), then call the object's toXML method.
  6. After each attribute, append an end tag for the attribute.
  7. After all attributes are XML'd, append the end tag for the object.
The drawback of this direct implementation approach is that someone needs to look at each class that might be affected and at the possible attributes and contained objects, code the routine and test it.

Another way to approach the task is to look at a specific class, determine whether it should be included in the XML output and go through each public accessor method to create the XML output. This approach requires coding and testing one class and an interface. The drawback is that each object is inspected at runtime to determine the attributes and objects to include. The usefulness of this approach is that adding automatic XMLability to a new class is trivial.

Another approach attacks the problem from the source side. You can parse through each class (either by file or by class), find the class definitions, build the toXML() and append the code to the class definition.

These two approaches highlight the classic problem of whether to build it now (at compile time) or build it later (at runtime).

This article takes the dynamically generated approach, using the methods to get the XML information. The methods could also have been written to use the class attributes, but this doesn't demonstrate enough of the Reflection classes. The procedure to dynamically create XML code is similar to the procedure to hard-code XMLability, except when dealing with embedded arrays and collections. The JDK 1.2 has the interface Collection, which unified accessing Vectors, Lists, and so on. This interface has the method toArray(), which returns an array representation of the Collection. To write a routine for arrays, you need to be aware that Java arrays don't have to be symmetrical. In Java you can have an array that can look like the array in Listing 3. This Array doesn't have memory allocated for myArray[1][2],myArray[1][3], and myArray[2][3] because those elements don't exist.

To create the XML for an array of unknown dimensions, a recursive method called parseArray was added. It was the trickiest part of the AutoXML class to get right. This recursive type routine is necessary to iterate over all of the objects.

Another point to mention about the design of AutoXML is that someone else might want to implement his or her own toXML() in certain classes. Automatic generation might be priceless at development time, but if performance needs to be improved, it's important to provide a way. By adding an interface XMLable, the AutoXML class now understands two interfaces: AutoXMLable and XMLable. if the XML of class is to be automatically generated, the class should implement AutoXMLable. If the class has its own implementation, XMLable should be used. Finally, if the object's class doesn't implement either interface or is null, it should be skipped.

When trying to build XML, it's important to have the ability to select the classes. Security classes are a good example of classes that shouldn't be able to be used automatically by the AutoXML class. This is accomplished by using two interfaces: AutoXMLable and XMLable.

If the class implements XMLable, the AutoXML class will call toString(). If the class implements AutoXMLable, the AutoXML class will build the XML for the class. If the class doesn't implement either interface, the class will be skipped and an empty string will be returned.

Another area that deserves attention is the invoke method in class Method. This allows a function to be dynamically called on an instantiated object. This is used to get the objects contained in the current object and could cause a potential problem if the class has a function similar to the one below:

public getNextNumber() {return
++currentNumber;}

The AutoXML class won't be aware the object is being changed and will go merrily on its way, so be careful about method names. A better name might have been nextNumber().

Example
The example is a set of very simple classes for Person, FullName and Address. The SampleXML class demonstrates the ease of use of the AutoXML class. The XML is given in Listing 4. Person and Address implement the AutoXMLable interface, allowing AutoXML to create the XML. FullName implements XMLable to provide its own way of creating the XML tag. The classes have been kept simple for demonstration purposes, but feel free to change the example classes. Change getAddresses() into an array... make two-dimensional arrays of Fullname. Experiment with the class definitions and see how the XML is generated.

A Walk in the Code...
The program begins in SampleXML::main(). This creates a Person named John Doe having two addresses.

  • Next, AutoXML::toXML() is called (passing the object to be AutoXML'd) and outputs the results.
  • toXML() first checks to ensure that the object passed is not null, gets the name of the object's class name, strips the package name,calls parseclass and then wraps the result in a tag.
  • parseClass() iterates over the methods in the class to determine if the method will return a useful piece of data. Following bean-naming guidelines, only the prefixes get and is (if the return type is Boolean) are understood. Also, the method must be public nonstatic. The method name is stripped of its prefix. if the return parameter is a primitive, a string or date, the tag is created for the attribute; otherwise the returned object is broken down into its subcomponents (in expandClass()) or array elements (parseArray()).
  • expandClass() if the return parameter is AutoXMLable, XMLable or Collectable. The method is invoked and the result is sent to toXML()
  • parseArray() takes an n-dimensional array and recursively reduces the dimension of the array until it is a one-dimensional array that can be iterated over. If the result isn't primitive, the array object is sent to toXML().
  • wrapTag() puts a tagname into parameters, and information into an XML tag.
What Isn't Covered in the Example
The reflection classes have a few very useful methods that weren't needed for the AutoXML class. The most powerful, Class.forName(String ClassName), is a simple method that returns the class object represented by the String passed.

In combination with the newInstance() method, an object can be created (calling the constructor without parameters) by knowing only the name of the class. This opens the door for dynamically adding functionality to a program without recompiling. But that's another article.

I also didn't cover security, determination of inheritance structure, class loading or hashing capabilities. Nor did I cover XSL, which describes the formatting of the XML data. Microsoft has a tool to combine XML and XSL to create HTML ouput. An equivalent AutoXSL class that is similar to the AutoXML class can be created. The difference is that XSL would create tags without the data being embedded. Listing 4 shows an example of XSL. Try to write the class yourself, using the AutoXML for the algorithms.

Conclusion
As the example shows, the reflection classes are extremely useful for adding functionality to a program. With these classes you can easily create generic routines, scripting engines and more dynamic programs.

About the Author
Daniel Rosengarten, a senior systems analyst at Sanford C. Bernstein, is involved in the development of several OO financial systems using C++, PowerBuilder, Visual Basic and Java. A Sun-certified Java developer, he has an MBA from the University of Arizona and a BS in electrical engineering. Daniel can be reached at [email protected]

 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.