Saturday, January 23, 2010

Groovy: Injecting New Methods on Existing Java Classes

One of Groovy's most significant "goodies" is the ability to intercept calls, even to standard Java classes, and either override existing methods or even add new methods dynamically. In this blog post, I will demonstrate how to add a method to the Calendar that provides the appropriate Zodiak Tropical Sign.

Groovy offers rich dynamic capabilities via its MetaClass support. In this blog post, I will take advantage of this feature to have a method called getZodiakTropicalSign() injected into the SDK-provided Calendar. The purpose of this method is to provide the applicable Zodiak sign for a given instance of Calendar.

The portion of this script most relevant to this post appears at the top of the script. The syntax Calendar.metaClass.getZodiakTropicalSign = { -> defines a new method getZodiakTrpopicalSign on Calendar and then uses delegate to work with the underlying instance of java.util.Calendar.



#!/usr/bin/env groovy

Calendar.metaClass.getZodiakTropicalSign = { ->
def humanMonth = delegate.get(Calendar.MONTH) + 1
def day = delegate.get(Calendar.DATE)
def zodiakTropicalSign = "Unknown"
switch (humanMonth)
{
case 1 :
zodiakTropicalSign = day < 20 ? "Capricorn" : "Aquarius"
break
case 2 :
zodiakTropicalSign = day < 19 ? "Aquarius" : "Pisces"
break
case 3 :
zodiakTropicalSign = day < 21 ? "Pisces" : "Aries"
break
case 4 :
zodiakTropicalSign = day < 20 ? "Aries" : "Taurus"
break
case 5 :
zodiakTropicalSign = day < 21 ? "Taurus" : "Gemini"
break
case 6 :
zodiakTropicalSign = day < 21 ? "Gemini" : "Cancer"
break
case 7 :
zodiakTropicalSign = day < 23 ? "Cancer" : "Leo"
break
case 8 :
zodiakTropicalSign = day < 23 ? "Leo" : "Virgo"
break
case 9 :
zodiakTropicalSign = day < 23 ? "Virgo" : "Libra"
break
case 10 :
zodiakTropicalSign = day < 23 ? "Libra" : "Scorpio"
break
case 11 :
zodiakTropicalSign = day < 22 ? "Scorpio" : "Sagittarius"
break
case 12 :
zodiakTropicalSign = day < 22 ? "Sagittarius" : "Capricorn"
break
}
return zodiakTropicalSign
}

def calendar = Calendar.instance
// First argument to script is expected to be month integer between 1 (January)
// and 12 (December). Second argument is expected to be the date of that month.
// If fewer than two arguments are provided, will use today's date (default) instead.
if (args.length > 1)
{
try
{
calendar.set(Calendar.MONTH, (args[0] as Integer) - 1)
calendar.set(Calendar.DATE, args[1] as Integer)
}
catch (NumberFormatException nfe)
{
println "Arguments ${args[0]} and ${args[1]} are not valid respective month and date integers."
println "Using today's date (${calendar.get(Calendar.MONTH)+1}/${calendar.get(Calendar.DATE)}) instead."
}
}
print "A person born on ${calendar.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.US)}"
println " ${calendar.get(Calendar.DATE)} has the Zodiak sign ${calendar.getZodiakTropicalSign()}"


The final line of the above script calls the injected getZodiakTropicalSign() method on Calendar. This is demonstrated in the next screen snapshot.



The first execution of the script in the above screen snapshot is executed without parameters and so it prints out the Zodiak Sign for today (23 January). The remainder of the script runs are for different dates. This script could have been made more user-friendly using Groovy's built-in CLI support, but the focus here is on the dynamically injected method.

Conclusion

Groovy allows the developer to inject methods on instances of existing classes even when the developer does not own or have control over the existing class. This can be a powerful capability to bending an existing class to one's will.

Thursday, January 21, 2010

Groovy: Switch on Steroids

UPDATE: This post underwent significant updates on 17 November 2016 to correct erroneous statements and examples, to fix the underlying HTML layout (not obvious to readers unless you view HTML source in a web browser), and to fix some spelling issues.


I have blogged before regarding Groovy's support for switching on String. Groovy can switch on much more than just literal Strings (and literal integral types that Java allows switching on) and I demonstrate this briefly here.

Groovy's switch statement will use a method implemented with the name "isCase" to determine if a particular switch option is matched. This means that custom objects are "switchable" in Groovy. For the simple example in this blog post, I'll use the Java classes SimpleState and State.java.

SimpleState.java

package dustin.examples;

import static java.lang.System.out;

/**
 * Java class to be used in demonstrating the "switch on steroids" in Groovy.
 * The Groovy script will be able to {@code switch} on instances of this class
 * via the implicit invocation of {@code toString()} if the {@code case}
 * statements use {@code String}s as the items to match.
 */
public class SimpleState
{
   private String stateName;

   public SimpleState(final String newStateName)
   {
      this.stateName = newStateName;
   }

   @Override
   public String toString()
   {
      return this.stateName;
   }
}

The above Java class's String representation can be switched on in a Groovy script as shown in the next code listing for switchOnSimpleState.groovy:

switchOnSimpleState.groovy

#!/usr/bin/env groovy

import dustin.examples.SimpleState

SimpleState state = new SimpleState("Colorado")
print "The motto for the state of ${state.stateName} is '"
switch (state)
{
   case "Alabama":      print "Audemus jura nostra defendere"
                        break
   case "Alaska":       print "North to the future"
                        break
   case "Arizona":      print "Ditat Deus"
                        break
   case "Arkansas":     print "Regnat populus"
                        break
   case "California":   print "Eureka"
                        break
   case "Colorado":     print "Nil sine numine"
                        break
   case "Connecticut":  print "Qui transtulit sustinet"
                        break
   default : print "<<State ${state.stateName} not found.>>"
}
println "'"

When the above Groovy script is run against the above simple Java class, the code prints out the correct information because Groovy implicitly invokes the toString() method on the "state" instance of State being switched on. Similar functionality can now be achieved in Java, but one needs to explicitly call toString() on the object being switched on. It's also worth keeping in mind that when I wrote the original version of this post in early 2010, Java did not support switching on Strings. The output of running the above is shown in the screen snapshot below (the name of the script doesn't match above because this is an old screen snapshot from this original post before it was corrected and updated).

With Groovy and the isCase method, I can switch on just about any data type I like. To demonstrate this, the Java class State will be used and its code listing is shown below. It includes a isCase(State) method that Groovy will implicitly call when instances of State are being switched against as the case choices. In this case, the isCase(State) method simply calls the State.equals(Object) method to determine if that case is true. Although this is the typical behavior for implementations of isCase(Object), we could have had it determine if it was the case or not in any way we wanted.

State.java

package dustin.examples;

import static java.lang.System.out;

public class State
{
   private String stateName;

   public State(final String newStateName)
   {
      this.stateName = newStateName;
   }

   /**
    * Method to be used by Groovy's switch implicitly when an instance of this
    * class is switched on.
    *
    * @param compareState State passed via case to me to be compared to me.
    */
   public boolean isCase(final State compareState)
   {
      return compareState != null ? compareState.equals(this) : false;
   }

   public boolean equals(final Object other)
   {
      if (!(other instanceof State))
      {
         return false;
      }
      final State otherState = (State) other;
      if (this.stateName == null ? otherState.stateName != null : !this.stateName.equals(otherState.stateName))
      {
         return false;
      }
      return true;
   }

   @Override
   public String toString()
   {
      return this.stateName;
   }
}

The simple standard Java class shown above implements an isCase method that will allow Groovy to switch on it. The following Groovy script uses this class and is able to successfully switch on the instance of State.

#!/usr/bin/env groovy

import dustin.examples.State
State state = new State("Arkansas")
State alabama = new State("Alabama")
State arkansas = new State("Arkansas")
State alaska = new State("Alaska")
State arizona = new State("Arizona")
State california = new State("California")
State colorado = new State("Colorado")
State connecticut = new State("Connecticut")

print "The motto for the state of ${state.stateName} is '"
switch (state)
{
   case alabama     : print "Audemus jura nostra defendere"
                      break
   case alaska      : print "North to the future"
                      break
   case arizona     : print "Ditat Deus"
                      break
   case arkansas    : print "Regnat populus"
                      break
   case california  : print "Eureka"
                      break
   case colorado    : print "Nil sine numine"
                      break
   case connecticut : print "Qui transtulit sustinet"
                      break
   default : print "<<State ${state.stateName} not found.>>"
}
println "'"

The output in the next screen snapshot indicates that the Groovy script is able to successfully switch on an instance of a State object. The first command is using the "simple" example discussed earlier and the second command is using the example that needs to invoke State's isCase(State) method.

The beauty of this ability to have classes be "switchable" based on the implementation of an isCase() method is that it allows more concise syntax in situations that otherwise might have required lengthy if/else if/else constructs. It's preferable to avoid such constructs completely, but sometimes we run into them and the Groovy switch statement makes them less tedious.

It is entirely possible with the Groovy switch to have multiple switch options match the specified conditions. Therefore, it is important to list the case statements in order of which match is preferred because the first match will be the one executed. The break keyword is used in Groovy's switch as it is in Java.

There is much more power in what the Groovy switch supports. Some posts that cover this power include Groovy Goodness: The Switch Statement, Groovy, let me count the ways in which I love thee, and the Groovy documentation.

European Commission Approves Oracle Acquisition of Sun

The European Commission issued a press release today (21 January 2010) stating:

The European Commission has approved under the EU Merger Regulation the proposed acquisition of US hardware and software vendor Sun Microsystems Inc. by Oracle Corporation, a US enterprise software company. After an in-depth examination, launched in September 2009 (see IP/09/1271), the Commission concluded that the transaction would not significantly impede effective competition in the European Economic Area (EEA) or any substantial part of it.



On MySQL

One of the sentences I found most interesting in this press release was actually not directly related to Java:

The Commission's investigation showed that another open source database, PostgreSQL, is considered by many database users to be a credible alternative to MySQL and could be expected to replace to some extent the competitive force currently exerted by MySQL on the database market.


As a developer who generally prefers PostgreSQL over MySQL, this is not news to me, but it is interesting (and appropriate) that the European Commission considered it.

The drama in the Oracle acquisition of Sun has hinged primarily on MySQL. I have blogged before on how hypocritical it has seemed for those who once sold off interest in MySQL (for a handsome profit) to try and use government to force the purchaser of that product to do with it as the original seller wants. Marc Fleury has gone even further, pointing out that these actions had potential to only hurt Sun and its employees worse and has the potential to damage the likelihood of corporations investing in open source. His words included the entirely true: "this is making OSS acquisitions look very dangerous and dicey." Charles H. Schulze also has excellent post on this.

I do think the save MySQL crusade has been counter-productive for the open source software movement and one of the positives of this acquisition will be the end of that destructive force.


The End of an Era

I am sorry to see the end of a company that has been partially or fully responsible for many great innovations and for making my life as a developer easier in many different ways. I realized my tangible sorrow over this when I saw James Gosling's tribute to Sun. The simplicity of the post, titled "So long, old friend" with the stirring image, successfully accomplished what I believe to be its intended point. It's worth viewing for anyone with significant interest in Java. You can also see the same image in the blog post Every Good Thing Has An End.

Wednesday, January 20, 2010

First Solid Look at Oracle + Sun?

Since the announcement of Oracle's intent to buy Sun 9 months ago (20 April 2009), many of us have been only able to speculate on what this means for all things Java. During this relatively long period of time, we've had some clues such as Oracle's advertisement aimed at "Sun Customers", the closely covered events and announcements at 2009 JavaOne, the Oracle Buys Sun slides, and the Reuters interview with Oracle founder and CEO Larry Ellison.

The European Union's concerns with Oracle's acquisition of Sun seem to have been addressed, so it is not all that surprising that we're starting to hear more. The potential for the most clear view into Oracle's plans for Sun's assets may lie in the just-announced Oracle+Sun Strategy event scheduled for 9 am to 2 pm Pacific Time on 27 January 2010 (the same day that is the EU's deadline for their regulatory approval of the acquisition). According to the press release, the event will be broadcast globally.

The press release also states that "product roadmaps" and the "strategy for the combined companies" will be outlined in this event. I am certain that the blogosphere and other media formats will be abuzz with coverage, analysis, conspiracy theories, innuendos, and so forth following this event, but I think many of us hope to better understand what this all means for the future of Java. Hopefully we will know quite a bit more about all of this within a week's time.

Tuesday, January 19, 2010

Reproducing "too many constants" Problem in Java

In my previous blog post, I blogged on the "code too large" problem and reproduced that error message. In this post, I look at the very similar "too many constants" error message (not the same thing as the question too many constants?) and demonstrate reproducing it by having too many methods in a generated Java class.

With a few small adaptations, I can adjust the Groovy script that I used to generate a Java class to reproduce the "code too large" error to instead generate a Java class to reproduce the "too many constants" error. Here is the revised script.

generateJavaClassWithManyMethods.groovy

#!/usr/bin/env groovy

import javax.tools.ToolProvider

println "You're running the script ${System.getProperty('script.name')}"
if (args.length < 2)
{
println "Usage: javaClassGenerationWithManyMethods packageName className baseDir #methods"
System.exit(-1)
}

// No use of "def" makes the variable available to entire script including the
// defined methods ("global" variables)

packageName = args[0]
packagePieces = packageName.tokenize(".") // Get directory names
def fileName = args[1].endsWith(".java") ? args[1] : args[1] + ".java"
def baseDirectory = args.length > 2 ? args[2] : System.getProperty("user.dir")
numberOfMethods = args.length > 3 ? Integer.valueOf(args[3]) : 10

NEW_LINE = System.getProperty("line.separator")

// The setting up of the indentations shows off Groovy's easy feature for
// multiplying Strings and Groovy's tie of an overloaded * operator for Strings
// to the 'multiply' method. In other words, the "multiply" and "*" used here
// are really the same thing.
SINGLE_INDENT = ' '
DOUBLE_INDENT = SINGLE_INDENT.multiply(2)
TRIPLE_INDENT = SINGLE_INDENT * 3

def outputDirectoryName = createDirectories(baseDirectory)
def generatedJavaFile = generateJavaClass(outputDirectoryName, fileName)
compileJavaClass(generatedJavaFile)


/**
* Generate the Java class and write its source code to the output directory
* provided and with the file name provided. The generated class's name is
* derived from the provided file name.
*
* @param outDirName Name of directory to which to write Java source.
* @param fileName Name of file to be written to output directory (should include
* the .java extension).
* @return Fully qualified file name of source file.
*/
def String generateJavaClass(outDirName, fileName)
{
def className = fileName.substring(0,fileName.size()-5)
outputFileName = outDirName.toString() + File.separator + fileName
outputFile = new File(outputFileName)
outputFile.write "package ${packageName};${NEW_LINE.multiply(2)}"
outputFile << "public class ${className}${NEW_LINE}"
outputFile << "{${NEW_LINE}"
outputFile << "${SINGLE_INDENT}public static void main(final String[] arguments)"
outputFile << "${NEW_LINE}${SINGLE_INDENT}{${NEW_LINE}"
outputFile << DOUBLE_INDENT << 'final String someString = "Dustin";' << NEW_LINE
outputFile << "${SINGLE_INDENT}}${NEW_LINE}"
outputFile << buildManyMethods()
outputFile << "}"
return outputFileName
}


/**
* Compile the provided Java source code file name.
*
* @param fileName Name of Java file to be compiled.
*/
def void compileJavaClass(fileName)
{
// Use the Java SE 6 Compiler API (JSR 199)
// http://java.sun.com/mailers/techtips/corejava/2007/tt0307.html#1
compiler = ToolProvider.getSystemJavaCompiler()

// The use of nulls in the call to JavaCompiler.run indicate use of defaults
// of System.in, System.out, and System.err.
int compilationResult = compiler.run(null, null, null, fileName)
if (compilationResult == 0)
{
println "${fileName} compiled successfully"
}
else
{
println "${fileName} compilation failed"
}
}


/**
* Create directories to which generated files will be written.
*
* @param baseDir The base directory used in which subdirectories for Java
* source packages will be generated.
*/
def String createDirectories(baseDir)
{
def outDirName = new StringBuilder(baseDir)
for (pkgDir in packagePieces)
{
outDirName << File.separator << pkgDir
}
outputDirectory = new File(outDirName.toString())
if (outputDirectory.exists() && outputDirectory.isDirectory())
{
println "Directory ${outDirName} already exists."
}
else
{
isDirectoryCreated = outputDirectory.mkdirs() // Use mkdirs in case multiple
println "Directory ${outputDirectoryName} ${isDirectoryCreated ? 'is' : 'not'} created."
}
return outDirName.toString()
}


/**
* Generate the body of generated Java class source code's main function.
*/
def String buildManyMethods()
{
def str = new StringBuilder() << NEW_LINE
for (i in 0..numberOfMethods)
{
str << SINGLE_INDENT << "private void doMethod${i}(){}" << NEW_LINE
}
return str
}


When the above script is run with a parameter of 5 for the number of methods, the following Java code is generated.


package dustin.examples;

public class LotsOfMethods
{
public static void main(final String[] arguments)
{
final String someString = "Dustin";
}

private void doMethod0(){}
private void doMethod1(){}
private void doMethod2(){}
private void doMethod3(){}
private void doMethod4(){}
private void doMethod5(){}
}


When I turn up the number of generated methods to 65000 methods, I run out of heap space as shown in the next screen snapshot.



The next screen snapshot shows the output of running the script again, but this time with 512 MB maximum heap space specified for the JVM.



What happens when we try to compile a class with too many methods? That is shown in the next screen snapshot that demonstrates what happens when just such a compilation is attempted.



The "too many constants" error message is shown with a pointer at the class keyword in the class definition. The method has too many methods to compile.

When I run javap -c -private dustin.examples.LotsOfMethods (-c to disassemble the code, -private to display the many private methods, and dustin.examples.LotsOfMethods is the name of the generated Java class), I see output like the following (only the first and end shown instead of displaying all 60,000+ methods).


Compiled from "LotsOfMethods.java"
public class dustin.examples.LotsOfMethods extends java.lang.Object{
public dustin.examples.LotsOfMethods();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."":()V
4: return

public static void main(java.lang.String[]);
Code:
0: return

private void doMethod0();
Code:
0: return

private void doMethod1();
Code:
0: return

private void doMethod2();
Code:
0: return

private void doMethod3();
Code:
0: return

private void doMethod4();
Code:
0: return

private void doMethod5();
Code:
0: return

private void doMethod6();
Code:
0: return

private void doMethod7();
Code:
0: return

private void doMethod8();
Code:
0: return

. . .

. . .

. . .

private void doMethod64992();
Code:
0: return

private void doMethod64993();
Code:
0: return

private void doMethod64994();
Code:
0: return

private void doMethod64995();
Code:
0: return

private void doMethod64996();
Code:
0: return

private void doMethod64997();
Code:
0: return

private void doMethod64998();
Code:
0: return

private void doMethod64999();
Code:
0: return

private void doMethod65000();
Code:
0: return

}



Conclusion

As with the last blog post, this post used Groovy and the Java Compiler API to intentionally reproduce an error that we hope to not see very often.


Additional Reference

Error Writing File: too many constants

Monday, January 18, 2010

Reproducing "code too large" Problem in Java

Code conventions and standard software development wisdom dictate that methods should not be too long because they become difficult to fully comprehend, they lose readability when they get too long, they are difficult to appropriately unit test, and they are difficult to reuse. Because most Java developers strive to write highly modular code with small, highly cohesive methods, the "code too large" error in Java is not seen very often. When this error is seen, it is often in generated code.

In this blog post, I intentionally force this "code too large" error to occur. Why in the world would one intentionally do this? In this case, it is because I always understand things better when I tinker with them rather than just reading about them and because doing so gives me a chance to demonstrate Groovy, the Java Compiler API (Java SE 6), and javap.

It turns out that the magic number for the "code too large" error is 65535 bytes (compiled byte code, not source code). Hand-writing a method large enough to lead to this size of a .class file would be tedious (and not worth the effort in my opinion). However, it is typically generated code that leads to this in the wild and so generation of code seems like the best approach to reproducing the problem. When I think of generic Java code generation, I think Groovy.

The Groovy script that soon follows generates a Java class that isn't very exciting. However, the class will have its main function be of an approximate size based on how many conditions I tell the script to create. This allows me to quickly try generating Java classes with different main() method sizes to ascertain when the main() becomes too large.

After the script generates the Java class, it also uses the Java Compiler API to automatically compile the newly generated Java class for me. The resultant .class file is placed in the same directory as the source .java file. The script, creatively named generateJavaClass.groovy, is shown next.

generateJavaClass.groovy

#!/usr/bin/env groovy

import javax.tools.ToolProvider

println "You're running the script ${System.getProperty('script.name')}"
if (args.length < 2)
{
println "Usage: javaClassGeneration packageName className baseDir #loops"
System.exit(-1)
}

// No use of "def" makes the variable available to entire script including the
// defined methods ("global" variables)

packageName = args[0]
packagePieces = packageName.tokenize(".") // Get directory names
def fileName = args[1].endsWith(".java") ? args[1] : args[1] + ".java"
def baseDirectory = args.length > 2 ? args[2] : System.getProperty("user.dir")
numberOfConditionals = args.length > 3 ? Integer.valueOf(args[3]) : 10

NEW_LINE = System.getProperty("line.separator")

// The setting up of the indentations shows off Groovy's easy feature for
// multiplying Strings and Groovy's tie of an overloaded * operator for Strings
// to the 'multiply' method. In other words, the "multiply" and "*" used here
// are really the same thing.
SINGLE_INDENT = ' '
DOUBLE_INDENT = SINGLE_INDENT.multiply(2)
TRIPLE_INDENT = SINGLE_INDENT * 3

def outputDirectoryName = createDirectories(baseDirectory)
def generatedJavaFile = generateJavaClass(outputDirectoryName, fileName)
compileJavaClass(generatedJavaFile)


/**
* Generate the Java class and write its source code to the output directory
* provided and with the file name provided. The generated class's name is
* derived from the provided file name.
*
* @param outDirName Name of directory to which to write Java source.
* @param fileName Name of file to be written to output directory (should include
* the .java extension).
* @return Fully qualified file name of source file.
*/
def String generateJavaClass(outDirName, fileName)
{
def className = fileName.substring(0,fileName.size()-5)
outputFileName = outDirName.toString() + File.separator + fileName
outputFile = new File(outputFileName)
outputFile.write "package ${packageName};${NEW_LINE.multiply(2)}"
outputFile << "public class ${className}${NEW_LINE}"
outputFile << "{${NEW_LINE}"
outputFile << "${SINGLE_INDENT}public static void main(final String[] arguments)"
outputFile << "${NEW_LINE}${SINGLE_INDENT}{${NEW_LINE}"
outputFile << DOUBLE_INDENT << 'final String someString = "Dustin";' << NEW_LINE
outputFile << buildMainBody()
outputFile << "${SINGLE_INDENT}}${NEW_LINE}"
outputFile << "}"
return outputFileName
}


/**
* Compile the provided Java source code file name.
*
* @param fileName Name of Java file to be compiled.
*/
def void compileJavaClass(fileName)
{
// Use the Java SE 6 Compiler API (JSR 199)
// http://java.sun.com/mailers/techtips/corejava/2007/tt0307.html#1
compiler = ToolProvider.getSystemJavaCompiler()

// The use of nulls in the call to JavaCompiler.run indicate use of defaults
// of System.in, System.out, and System.err.
int compilationResult = compiler.run(null, null, null, fileName)
if (compilationResult == 0)
{
println "${fileName} compiled successfully"
}
else
{
println "${fileName} compilation failed"
}
}


/**
* Create directories to which generated files will be written.
*
* @param baseDir The base directory used in which subdirectories for Java
* source packages will be generated.
*/
def String createDirectories(baseDir)
{
def outDirName = new StringBuilder(baseDir)
for (pkgDir in packagePieces)
{
outDirName << File.separator << pkgDir
}
outputDirectory = new File(outDirName.toString())
if (outputDirectory.exists() && outputDirectory.isDirectory())
{
println "Directory ${outDirName} already exists."
}
else
{
isDirectoryCreated = outputDirectory.mkdirs() // Use mkdirs in case multiple
println "Directory ${outputDirectoryName} ${isDirectoryCreated ? 'is' : 'not'} created."
}
return outDirName.toString()
}


/**
* Generate the body of generated Java class source code's main function.
*/
def String buildMainBody()
{
def str = new StringBuilder() << NEW_LINE
str << DOUBLE_INDENT << "if (someString == null || someString.isEmpty())" << NEW_LINE
str << DOUBLE_INDENT << "{" << NEW_LINE
str << TRIPLE_INDENT << 'System.out.println("The String is null or empty.");'
str << NEW_LINE << DOUBLE_INDENT << "}" << NEW_LINE
for (i in 0..numberOfConditionals)
{
str << DOUBLE_INDENT << 'else if (someString.equals("a' << i << '"))' << NEW_LINE
str << DOUBLE_INDENT << "{" << NEW_LINE
str << TRIPLE_INDENT << 'System.out.println("You found me!");' << NEW_LINE
str << DOUBLE_INDENT << "}" << NEW_LINE
}
str << DOUBLE_INDENT << "else" << NEW_LINE
str << DOUBLE_INDENT << "{" << NEW_LINE
str << TRIPLE_INDENT << 'System.out.println("No matching string found.");'
str << DOUBLE_INDENT << NEW_LINE << DOUBLE_INDENT << "}" << NEW_LINE
return str
}


Because this script is intended primarily for generating Java code to learn more about the "code too large" error and to demonstrate a few things, I did not make it nearly as fancy as it could be. For one thing, I did not use Groovy's built-in Apache CLI support for handling command-line arguments as I have demonstrated in previous blog posts on using Groovy to check seventh grade homework.

Even though the script above does not apply Groovy's full potential, it still manages to demonstrate some Groovy niceties. I tried to add comments in the script describing some of these. These include features such as Groovy GDK's String.tokenize method and other useful Groovy String extensions.

When I run this script from the directory C:\java\examples\groovyExamples\javaClassGeneration with the arguments "dustin.examples" (package structure), "BigClass" (name of generated Java class), "." (current directory is based directory for generated code, and "5" (number of conditionals to be in generated code), the script's output is shown here and in the following screen snapshot:


You're running the script C:\java\examples\groovyExamples\javaClassGeneration\generateJavaClass.groovy
Directory .\dustin\examples already exists.
.\dustin\examples\BigClass.java compiled successfully




This output tells us that the generated Java class with five conditionals compiled successfully. To get a taste of what this generated Java class looks like, we'll look at this newly generated version with only five conditionals.

BigClass.java (generated with 5 conditionals)

package dustin.examples;

public class BigClass
{
public static void main(final String[] arguments)
{
final String someString = "Dustin";

if (someString == null || someString.isEmpty())
{
System.out.println("The String is null or empty.");
}
else if (someString.equals("a0"))
{
System.out.println("You found me!");
}
else if (someString.equals("a1"))
{
System.out.println("You found me!");
}
else if (someString.equals("a2"))
{
System.out.println("You found me!");
}
else if (someString.equals("a3"))
{
System.out.println("You found me!");
}
else if (someString.equals("a4"))
{
System.out.println("You found me!");
}
else if (someString.equals("a5"))
{
System.out.println("You found me!");
}
else
{
System.out.println("No matching string found.");
}
}
}


The above code includes two default conditionals every time regardless of how many conditionals are selected when the class generation script is run. In between the check for null/empty String and the else clause if no other else if has been satisfied are the number of else if statements specified when the class generation script was run. In this case, 5 was that number and so there are five else if conditionals between the two default conditionals on either end. As this demonstrates, it will be easy to scale up the number of conditionals until the Java compiler just won't take it anymore.

I now try the Groovy script for generating the Java class again, but this time go all out and select 5000 as the number of desired conditionals. As the output shown below and in the following screen snapshot indicate, Groovy has no trouble generating the text file representing the Java class with this many conditionals in its main() function, but the Java compiler doesn't like it one bit.


You're running the script C:\java\examples\groovyExamples\javaClassGeneration\generateJavaClass.groovy
Directory .\dustin\examples already exists.
.\dustin\examples\BigClass.java:5: code too large
public static void main(final String[] arguments)
^
1 error
.\dustin\examples\BigClass.java compilation failed




Obviously, the attempt to compile the generated class with a 5000+2 conditional main was too much. Through a little iterative trial-and-error, I was able to determine that 2265 conditionals (beyond the two defaults) was the maximum compilable number for my main() function and 2266 would break it. This is demonstrated in the next screen snapshot.



Knowing our limits better, we can now "look" at the byte code using the javap tool provided with Sun's JDK to analyze the corresponding class file. Because there was a compiler error when we tried to compile the code with 2266 additional conditionals, we must run javap against the BigClass.class file generated with 2265 additional conditionals. The output of running javap with the -c option for this large class is too large (~1 MB) to bludgeon readers with here. However, I include key snippets from its output below.


Compiled from "BigClass.java"
public class dustin.examples.BigClass extends java.lang.Object{
public dustin.examples.BigClass();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return

public static void main(java.lang.String[]);
Code:
0: ldc #2; //String Dustin
2: ifnonnull 10
5: goto_w 23
10: ldc #2; //String Dustin
12: invokevirtual #3; //Method java/lang/String.isEmpty:()Z
15: ifne 23
18: goto_w 36
23: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
26: ldc #5; //String The String is null or empty.
28: invokevirtual #6; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
31: goto_w 65512
36: ldc #2; //String Dustin
38: ldc #7; //String a0
40: invokevirtual #8; //Method java/lang/String.equals:(Ljava/lang/Object;)Z
43: ifne 51
46: goto_w 64
51: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
54: ldc #9; //String You found me!
56: invokevirtual #6; //Method java/io/PrintStream.println:


. . .

. . .

. . .

65411: goto_w 65512
65416: ldc #2; //String Dustin
65418: ldc_w #2272; //String a2263
65421: invokevirtual #8; //Method java/lang/String.equals:(Ljava/lang/Object;)Z
65424: ifne 65432
65427: goto_w 65445
65432: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
65435: ldc #9; //String You found me!
65437: invokevirtual #6; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
65440: goto_w 65512
65445: ldc #2; //String Dustin
65447: ldc_w #2273; //String a2264
65450: invokevirtual #8; //Method java/lang/String.equals:(Ljava/lang/Object;)Z
65453: ifne 65461
65456: goto_w 65474
65461: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
65464: ldc #9; //String You found me!
65466: invokevirtual #6; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
65469: goto_w 65512
65474: ldc #2; //String Dustin
65476: ldc_w #2274; //String a2265
65479: invokevirtual #8; //Method java/lang/String.equals:(Ljava/lang/Object;)Z
65482: ifne 65490
65485: goto_w 65503
65490: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
65493: ldc #9; //String You found me!
65495: invokevirtual #6; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
65498: goto_w 65512
65503: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
65506: ldc_w #2275; //String No matching string found.
65509: invokevirtual #6; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
65512: return
}


From the snippets of javap output shown above, we see that the highest Code offset (65512) for this function pushing the limits of the method size was getting awfully close to the magic 65535 bytes (216-1 or Short.MAX_VALUE - Short.MIN_VALUE).


Conclusion

Most Java developers don't see the "code too large" problem very often because they write methods and classes that are reasonable in size (or at least more reasonable than the limits allow). However, generated code can much more easily exceed these "limitations." So of what value is intentionally reproducing this error? Well, the next time someone tries to convince you that bigger is better, you can refer that person to this post.


Other Resources

⇒ Java Language Specification, Third Edition

⇒ Class File Format

⇒ Code Too Large for Try Statement?

⇒ Code Too Long

⇒ Is There Any Number of Lines Limit in a Java Class?

Thursday, January 14, 2010

Float/Double to BigDecimal in Groovy

In a previous blog post, I looked at the subtle handling required to handle double with BigDecimal. As I discussed in that post, using the BigDecimal(double) constructor rarely does what one would expect when passed a float or double. For a double, the Java developer is typically better off using BigDecimal.valueOf(double) or converting the double to a String and then using the BigDecimal(String) constructor. For a float, the String approach is typically the only desirable approach for getting a float to a BigDecimal in Java. With Groovy's dynamic typing and automatic BigDecimal use, these subtleties can be abstracted away from the Groovy developer in many cases.

The following simple Groovy script is based on the Java class in the previous blog post, but it is now "Groovier" and has a few more things demonstrated.


//import java.math.BigDecimal;

/**
* Simple example of problems associated with using BigDecimal constructor
* accepting a double.
*
* http://marxsoftware.blogspot.com/
*/
NEW_LINE = System.getProperty("line.separator")

//
// Demonstrate BigDecimal from double
//
double primitiveDouble = 0.1
BigDecimal bdPrimDoubleCtor = new BigDecimal(primitiveDouble)
BigDecimal bdPrimDoubleValOf = BigDecimal.valueOf(primitiveDouble)
Double referenceDouble = Double.valueOf(0.1)
BigDecimal bdRefDoubleCtor = new BigDecimal(referenceDouble)
BigDecimal bdRefDoubleValOf = BigDecimal.valueOf(referenceDouble)

println "Primitive Double: ${primitiveDouble}"
println "Reference Double: ${referenceDouble}"
println "Primitive BigDecimal/Double via Double Ctor: ${bdPrimDoubleCtor}"
println "Reference BigDecimal/Double via Double Ctor: ${bdRefDoubleCtor}"
println "Primitive BigDecimal/Double via ValueOf: ${bdPrimDoubleValOf}"
println "Reference BigDecimal/Double via ValueOf: ${bdRefDoubleValOf}"

println NEW_LINE

//
// Demonstrate BigDecimal from float
//
float primitiveFloat = 0.1f
BigDecimal bdPrimFloatCtor = new BigDecimal(primitiveFloat)
BigDecimal bdPrimFloatValOf = BigDecimal.valueOf(primitiveFloat)
Float referenceFloat = Float.valueOf(0.1f)
BigDecimal bdRefFloatCtor = new BigDecimal(referenceFloat)
BigDecimal bdRefFloatValOf = BigDecimal.valueOf(referenceFloat)

print "Primitive Float: ${primitiveFloat}"
println " (${primitiveFloat.class})"
print "Reference Float: ${referenceFloat}"
println " (${referenceFloat.class})"
print "Primitive BigDecimal/Float via Double Ctor: ${bdPrimFloatCtor}"
println " (${bdPrimFloatCtor.class})"
print "Reference BigDecimal/Float via Double Ctor: ${bdRefFloatCtor}"
println " (${bdRefFloatCtor.class})"
print "Primitive BigDecimal/Float via ValueOf: ${bdPrimFloatValOf}"
println " (${bdPrimFloatValOf.class})"
print "Reference BigDecimal/Float via ValueOf: ${bdRefFloatValOf}"
println " (${bdRefFloatValOf.class})"

println NEW_LINE

//
// More evidence of issues casting from float to double.
//
double primitiveDoubleFromFloat = 0.1f
Double referenceDoubleFromFloat = new Double(0.1f)
double primitiveDoubleFromFloatDoubleValue = new Float(0.1f).doubleValue()

print "Primitive Double from Float: ${primitiveDoubleFromFloat}"
println " (${primitiveDoubleFromFloat.class})"
print "Reference Double from Float: ${referenceDoubleFromFloat}"
println " (${referenceDoubleFromFloat.class}"
print "Primitive Double from FloatDoubleValue: ${primitiveDoubleFromFloatDoubleValue}"
println " (${primitiveDoubleFromFloatDoubleValue.class})"

println NEW_LINE

//
// Using String to maintain precision from float to BigDecimal
//
String floatString = String.valueOf(new Float(0.1f))
BigDecimal bdFromFloatViaString = new BigDecimal(floatString)
print "BigDecimal from Float via String.valueOf(): ${bdFromFloatViaString}"
println " (${bdFromFloatViaString.class})"

println NEW_LINE

//
// Using "duck typing"
//
def decimalWithoutStaticType = 0.1
print "Decimal Without Static Type: ${decimalWithoutStaticType}"
println " (${decimalWithoutStaticType.class})"
def floatWithoutStaticType = 0.1f
print "Float Without Static Type: ${floatWithoutStaticType}"
println " (${floatWithoutStaticType.class})"
def explicitDoubleWithoutStaticType = 0.1d
print "Explicit Double Without Static Type: ${explicitDoubleWithoutStaticType}"
println " (${explicitDoubleWithoutStaticType.class})"


The Groovier script shown above demonstrates that there is no need in Groovy to explicitly import java.math.BigDecimal. The script also has several of the classes's Class definitions printed out to show how Groovy treats these classes.

The following output is generated when this script is executed.



There are several observations one can make from this output. One, Groovy allows variables to be statically typed. When they are statically typed, they are essentially treated exactly as they are in Java with precision loss and all. Two, Groovy's dynamic typing automatically applies BigDecimal to floating-point numbers if no specific type is specified. Three, as the last few lines of the output demonstrate, Groovy prints out the value of 0.1 properly even when it reports Float, Double, or BigDecimal. In other words, the value of 0.1 is treated consistently as exactly 0.1 as long as the static types of float and double were not explicitly specified when declaring the variable. Now that's nice.


Conclusion

Groovy simplifies many of the nuances of floating-point arithmetic and representation. The Groovy Math documentation page calls Groovy's approach to mathematical operations a "least surprising" approach. That's something we can all appreciate.


Additional Resources

The following online resources contain further information about BigDecimal and Groovy.

⇒ Getting Around BigDecimal Pain with Groovy

⇒ The Evil BigDecimal Constructor

⇒ Make Cents with BigDecimal

⇒ The Need for BigDecimal

⇒ Why is the BigDecimal(double) Construction Still Around?