Musings about Java and Swing

Writing Java Code that Writes Java Code

A few weeks ago, someone asked this question on Stack Overflow.

Is [a] different way of accessing a Java Map possible?

If you scroll down, you can see my (mostly ignored) answer. Here’s a sentence from that answer.

“Thinking about the problem some more, you could write a Java application to read a Map and generate this class. With a method map code generator, this structure would be practical for a larger number of map rows.”

I had previously written Java code before that generates a Java class. It’s not all that hard, once someone shows you how. You just write a Java application to generate a text file that happens to be valid Java.

So I decided to write a Java application that writes a Java class. And I had just the class in mind.

Here’s the code.

package com.ggl.systemproperty;

public class SystemProperty {

	public static String awtNativeDoubleBuffering =
			getProperty("awt.nativeDoubleBuffering", "");
	public static String awtToolkit =
			getProperty("awt.toolkit", "");
	public static String comIbmCpuEndian =
			getProperty("com.ibm.cpu.endian", "");
	public static String comIbmOtiConfiguration =
			getProperty("com.ibm.oti.configuration", "");
	public static String comIbmOtiJclBuild =
			getProperty("com.ibm.oti.jcl.build", "");
	public static String comIbmOtiVmBootstrapLibraryPath =
			getProperty("com.ibm.oti.vm.bootstrap.library.path", "");
	public static String comIbmOtiVmLibraryVersion =
			getProperty("com.ibm.oti.vm.library.version", "");
	public static String comIbmUtilExtralibsProperties =
			getProperty("com.ibm.util.extralibs.properties", "");
	public static String comIbmVmBitmode =
			getProperty("com.ibm.vm.bitmode", "");
	public static String fileEncoding =
			getProperty("file.encoding", "");
	public static String fileEncodingPkg =
			getProperty("file.encoding.pkg", "");
	public static String fileSeparator =
			getProperty("file.separator", "");
	public static String ftpNonProxyHosts =
			getProperty("ftp.nonProxyHosts", "");
	public static String gnuClasspathHome =
			getProperty("gnu.classpath.home", "");
	public static String gnuClasspathHomeUrl =
			getProperty("gnu.classpath.home.url", "");
	public static String gnuClasspathVersion =
			getProperty("gnu.classpath.version", "");
	public static String gnuClasspathVmShortname =
			getProperty("gnu.classpath.vm.shortname", "");
	public static String gnuCpuEndian =
			getProperty("gnu.cpu.endian", "");
	public static String gnuGcjPrecompiledDbPath =
			getProperty("gnu.gcj.precompiled.db.path", "");
	public static String gnuGcjProgname =
			getProperty("gnu.gcj.progname", "");
	public static String gnuGcjRuntimeEndorsedDirs =
			getProperty("gnu.gcj.runtime.endorsed.dirs", "");
	public static String gnuGcjUserRealname =
			getProperty("gnu.gcj.user.realname", "");
	public static String gnuJavaUtilZoneinfoDir =
			getProperty("gnu.java.util.zoneinfo.dir", "");
	public static String gopherProxySet =
			getProperty("gopherProxySet", "");
	public static String groovyHome =
			getProperty("groovy.home", "");
	public static String groovyStarterConf =
			getProperty("groovy.starter.conf", "");
	public static String httpAgent =
			getProperty("http.agent", "");
	public static String httpNonProxyHosts =
			getProperty("http.nonProxyHosts", "");
	public static String ibmSignalhandlingRs =
			getProperty("ibm.signalhandling.rs", "");
	public static String ibmSignalhandlingSigchain =
			getProperty("ibm.signalhandling.sigchain", "");
	public static String ibmSignalhandlingSigint =
			getProperty("ibm.signalhandling.sigint", "");
	public static String ibmSystemEncoding =
			getProperty("ibm.system.encoding", "");
	public static String invokedviajava =
			getProperty("invokedviajava", "");
	public static String javaAssistive =
			getProperty("java.assistive", "");
	public static String javaAwtFonts =
			getProperty("java.awt.fonts", "");
	public static String javaAwtGraphicsenv =
			getProperty("java.awt.graphicsenv", "");
	public static String javaAwtHeadless =
			getProperty("java.awt.headless", "");
	public static String javaAwtPrinterjob =
			getProperty("java.awt.printerjob", "");
	public static String javaClassPath =
			getProperty("java.class.path", "");
	public static String javaClassVersion =
			getProperty("java.class.version", "");
	public static String javaCompiler =
			getProperty("java.compiler", "");
	public static String javaEndorsedDirs =
			getProperty("java.endorsed.dirs", "");
	public static String javaExtDirs =
			getProperty("java.ext.dirs", "");
	public static String javaFullversion =
			getProperty("java.fullversion", "");
	public static String javaHome =
			getProperty("java.home", "");
	public static String javaIoTmpdir =
			getProperty("java.io.tmpdir", "");
	public static String javaJclVersion =
			getProperty("java.jcl.version", "");
	public static String javaLibraryPath =
			getProperty("java.library.path", "");
	public static String javaRuntimeName =
			getProperty("java.runtime.name", "");
	public static String javaRuntimeVersion =
			getProperty("java.runtime.version", "");
	public static String javaSpecificationName =
			getProperty("java.specification.name", "");
	public static String javaSpecificationVendor =
			getProperty("java.specification.vendor", "");
	public static String javaSpecificationVersion =
			getProperty("java.specification.version", "");
	public static String javaUtilPrefsPreferencesFactory =
			getProperty("java.util.prefs.PreferencesFactory", "");
	public static String javaVendor =
			getProperty("java.vendor", "");
	public static String javaVendorUrl =
			getProperty("java.vendor.url", "");
	public static String javaVendorUrlBug =
			getProperty("java.vendor.url.bug", "");
	public static String javaVersion =
			getProperty("java.version", "");
	public static String javaVmInfo =
			getProperty("java.vm.info", "");
	public static String javaVmName =
			getProperty("java.vm.name", "");
	public static String javaVmServer =
			getProperty("java.vm.server", "");
	public static String javaVmSpecificationName =
			getProperty("java.vm.specification.name", "");
	public static String javaVmSpecificationVendor =
			getProperty("java.vm.specification.vendor", "");
	public static String javaVmSpecificationVersion =
			getProperty("java.vm.specification.version", "");
	public static String javaVmVendor =
			getProperty("java.vm.vendor", "");
	public static String javaVmVersion =
			getProperty("java.vm.version", "");
	public static String jxeCurrentRomimageVersion =
			getProperty("jxe.current.romimage.version", "");
	public static String jxeLowestRomimageVersion =
			getProperty("jxe.lowest.romimage.version", "");
	public static String lineSeparator =
			getProperty("line.separator", "");
	public static String mrjVersion =
			getProperty("mrj.version", "");
	public static String osArch =
			getProperty("os.arch", "");
	public static String osFamily =
			getProperty("os.family", "");
	public static String osName =
			getProperty("os.name", "");
	public static String osVersion =
			getProperty("os.version", "");
	public static String pathSeparator =
			getProperty("path.separator", "");
	public static String programName =
			getProperty("program.name", "");
	public static String socksNonProxyHosts =
			getProperty("socksNonProxyHosts", "");
	public static String sunArchDataModel =
			getProperty("sun.arch.data.model", "");
	public static String sunAwtDisableMixing =
			getProperty("sun.awt.disableMixing", "");
	public static String sunAwtNoerasebackground =
			getProperty("sun.awt.noerasebackground", "");
	public static String sunAwtXembedserver =
			getProperty("sun.awt.xembedserver", "");
	public static String sunBootClassPath =
			getProperty("sun.boot.class.path", "");
	public static String sunBootLibraryPath =
			getProperty("sun.boot.library.path", "");
	public static String sunCpuEndian =
			getProperty("sun.cpu.endian", "");
	public static String sunCpuIsalist =
			getProperty("sun.cpu.isalist", "");
	public static String sunDesktop =
			getProperty("sun.desktop", "");
	public static String sunIoUnicodeEncoding =
			getProperty("sun.io.unicode.encoding", "");
	public static String sunJavaCommand =
			getProperty("sun.java.command", "");
	public static String sunJavaLauncher =
			getProperty("sun.java.launcher", "");
	public static String sunJava2dFontpath =
			getProperty("sun.java2d.fontpath", "");
	public static String sunJnuEncoding =
			getProperty("sun.jnu.encoding", "");
	public static String sunManagementCompiler =
			getProperty("sun.management.compiler", "");
	public static String sunOsPatchLevel =
			getProperty("sun.os.patch.level", "");
	public static String userCountry =
			getProperty("user.country", "");
	public static String userDir =
			getProperty("user.dir", "");
	public static String userHome =
			getProperty("user.home", "");
	public static String userLanguage =
			getProperty("user.language", "");
	public static String userName =
			getProperty("user.name", "");
	public static String userRegion =
			getProperty("user.region", "");
	public static String userScript =
			getProperty("user.script", "");
	public static String userTimezone =
			getProperty("user.timezone", "");
	public static String userVariant =
			getProperty("user.variant", "");
	public static String userZoneinfoDir =
			getProperty("user.zoneinfo.dir", "");

	private static String getProperty(String key, String defaultString) {
		try {
			String s = System.getProperty(key);
			return (s != null) ? s : defaultString;
		} catch (SecurityException e) {
			return defaultString;
		}
	}

}

Thanks to Molindo and the article, The Final Take On Java System Properties, for the list of Java System properties found on various operating systems.

The static getProperty method makes the rest of the class trivial. Each static string returns either the System property value, or the default (empty) string. You don’t have to worry about a Security Exception or a null value.

To use any of these static strings, you just reference them. SystemProperty.lineSeparator gives you the value of the System property line.separator. The advantage to using the SystemProperty class over the System class is that an IDE like Eclipse will prompt you after you type in the first few characters. You never have to worry about misspelling a System parameter key ever again.

Here’s some code to test the SystemProperty class.

package com.ggl.systemproperty;

public class SystemPropertyTester {

	public static void main(String[] args) {
		System.out.println(SystemProperty.javaRuntimeName);
		System.out.println(SystemProperty.osName);
		System.out.println(SystemProperty.osVersion);
		System.out.println(SystemProperty.osFamily);
		System.out.println(SystemProperty.osArch);
	}

}

While SystemProperty is a useful Java class, this class would be a pain to maintain manually. Fortunately, there’s an easier way to maintain this Java class. Molindo, if you’re reading this, feel free to borrow this idea.

First, we create a Java application that gets all the System properties from my computer, so I have an initial list of properties to start with.

Here’s the code to create the initial System properties list.

package com.ggl.systemproperty;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

public class BuildPropertyFile implements Runnable {

	@Override
	public void run() {
		writePropertyList(buildPropertyList());
	}
	
	private List<String> buildPropertyList() {
		List<String> list = new ArrayList<String>();
		Properties keys = System.getProperties();
		Set<Map.Entry<Object,Object>> keySet = keys.entrySet();
		Iterator<Entry<Object, Object>> iter = keySet.iterator();
		while (iter.hasNext()) {
			Entry<Object, Object> keyEntry = iter.next();
			String s = (String) keyEntry.getKey();
			list.add(s);
		}
		return list;
	}
	
	private void writePropertyList(List<String> list) {
		String path = "C:/Eclipse/eclipse-4.2-work/" + 
				"com.ggl.systemproperty/properties.txt";
		try {
			BufferedWriter writer = 
					new BufferedWriter(new FileWriter(path));
			for (String s : list) {
				writer.write(s);
				writer.newLine();
			}
			writer.flush();
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		new BuildPropertyFile().run();
	}

}

And here’s the text file created from this Java application. 49 System properties.

java.runtime.name
sun.boot.library.path
java.vm.version
java.vm.vendor
java.vendor.url
path.separator
java.vm.name
file.encoding.pkg
user.country
user.script
sun.java.launcher
sun.os.patch.level
java.vm.specification.name
user.dir
java.runtime.version
java.awt.graphicsenv
java.endorsed.dirs
os.arch
java.io.tmpdir
line.separator
java.vm.specification.vendor
user.variant
os.name
sun.jnu.encoding
java.library.path
java.specification.name
java.class.version
sun.management.compiler
os.version
user.home
user.timezone
java.awt.printerjob
file.encoding
java.specification.version
java.class.path
user.name
java.vm.specification.version
sun.java.command
java.home
sun.arch.data.model
user.language
java.specification.vendor
awt.toolkit
java.vm.info
java.version
java.ext.dirs
sun.boot.class.path
java.vendor
file.separator

After reading and rereading through Molindo’s code, I get the full list of System properties.

java.runtime.name
sun.boot.library.path
java.vm.version
java.vm.vendor
java.vendor.url
path.separator
java.vm.name
file.encoding.pkg
user.country
user.script
sun.java.launcher
sun.os.patch.level
java.vm.specification.name
user.dir
java.runtime.version
java.awt.graphicsenv
java.endorsed.dirs
os.arch
java.io.tmpdir
line.separator
java.vm.specification.vendor
user.variant
os.name
sun.jnu.encoding
java.library.path
java.specification.name
java.class.version
sun.management.compiler
os.version
user.home
user.timezone
java.awt.printerjob
file.encoding
java.specification.version
java.class.path
user.name
java.vm.specification.version
sun.java.command
java.home
sun.arch.data.model
user.language
java.specification.vendor
awt.toolkit
java.vm.info
java.version
java.ext.dirs
sun.boot.class.path
java.vendor
file.separator
java.vendor.url.bug
sun.io.unicode.encoding
sun.cpu.endian
sun.desktop
sun.cpu.isalist
awt.nativeDoubleBuffering
com.ibm.cpu.endian
com.ibm.oti.configuration
com.ibm.oti.jcl.build
com.ibm.oti.vm.bootstrap.library.path
com.ibm.oti.vm.library.version
com.ibm.util.extralibs.properties
com.ibm.vm.bitmode
ftp.nonProxyHosts
gnu.classpath.home
gnu.classpath.home.url
gnu.classpath.version
gnu.classpath.vm.shortname
gnu.cpu.endian
gnu.gcj.precompiled.db.path
gnu.gcj.progname
gnu.gcj.runtime.endorsed.dirs
gnu.gcj.user.realname
gnu.java.util.zoneinfo.dir
gopherProxySet
groovy.home
groovy.starter.conf
http.agent
http.nonProxyHosts
ibm.signalhandling.rs
ibm.signalhandling.sigchain
ibm.signalhandling.sigint
ibm.system.encoding
invokedviajava
java.assistive
java.awt.fonts
java.jcl.version
java.util.prefs.PreferencesFactory
java.awt.graphicsenv
java.awt.printerjob
java.fullversion
java.awt.headless
java.compiler
java.vm.server
jxe.current.romimage.version
jxe.lowest.romimage.version
mrj.version
os.family
program.name
socksNonProxyHosts
sun.awt.disableMixing
sun.awt.noerasebackground
sun.awt.xembedserver
sun.java2d.fontpath
user.region
user.zoneinfo.dir

A list like this is much easier to maintain than the SystemProperties Java class I showed you earlier. You just add new properties and local properties your Java application(s) create to the bottom of the list and regenerate the SystemProperties class. Change the few default values from an empty string to your preferred default, and you’re done.

So, here’s the magic Java class that converts a list of System properties into a Java class.

package com.ggl.systemproperty;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Set;
import java.util.TreeSet;

public class CreatePropertyClass implements Runnable {
	
	private static final String lineSeparator =
			System.getProperty("line.separator");

	@Override
	public void run() {
		writeClass(buildClass());
	}

	private String buildClass() {
		Set<String> set = readProperties();
		
		StringBuilder builder = new StringBuilder();
		builder.append(setPackage());
		builder.append(lineSeparator);
		builder.append(setClass());
		builder.append(lineSeparator);
		
		for (String s : set) {
			builder.append(setField(s));
		}
		
		builder.append(lineSeparator);
		builder.append(setGetProperty());
		builder.append(setTry());
		builder.append(setString());
		builder.append(setReturn());
		builder.append(setCatch());
		builder.append(setCatchReturn());
		builder.append(end(2));
		builder.append(end(1));
		builder.append(lineSeparator);
		builder.append(end(0));
		builder.append(lineSeparator);
		
		return builder.toString();
	}
	
	private Set<String> readProperties() {
		String path = "C:/Eclipse/eclipse-4.2-work/" + 
				"com.ggl.systemproperty/properties.txt";
		Set<String> set = new TreeSet<String>();
		try {
			BufferedReader reader = 
					new BufferedReader(new FileReader(path));
			String line;
			while ((line = reader.readLine()) != null) {
				set.add(line);
			}
			reader.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return set;
	}
	
	private StringBuilder setPackage() {
		StringBuilder builder = new StringBuilder();
		builder.append("package com.ggl.systemproperty;");
		builder.append(lineSeparator);
		return builder;
	}
	
	private StringBuilder setClass() {
		StringBuilder builder = new StringBuilder();
		builder.append("public class SystemProperty {");
		builder.append(lineSeparator);
		return builder;
	}
	
	private StringBuilder setField(String s) {
		StringBuilder builder = new StringBuilder();
		builder.append(setTabs(1));
		builder.append("public static String ");
		builder.append(convertString(s));
		builder.append(" =");
		builder.append(lineSeparator);
		builder.append(setTabs(3));
		builder.append("getProperty(\"");
		builder.append(s);
		builder.append("\", \"\");");
		builder.append(lineSeparator);
		return builder;
	}
	
	private StringBuilder setGetProperty() {
		StringBuilder builder = new StringBuilder();
		builder.append(setTabs(1));
		builder.append("private static String getProperty");
		builder.append("(String key, String defaultString) {");
		builder.append(lineSeparator);
		return builder;
	}
	
	private StringBuilder setTry() {
		StringBuilder builder = new StringBuilder();
		builder.append(setTabs(2));
		builder.append("try {");
		builder.append(lineSeparator);
		return builder;
	}
	
	private StringBuilder setString() {
		StringBuilder builder = new StringBuilder();
		builder.append(setTabs(3));
		builder.append("String s = System.getProperty(key);");
		builder.append(lineSeparator);
		return builder;
	}
	
	private StringBuilder setReturn() {
		StringBuilder builder = new StringBuilder();
		builder.append(setTabs(3));
		builder.append("return (s != null) ? s : defaultString;");
		builder.append(lineSeparator);
		return builder;
	}
	
	private StringBuilder setCatch() {
		StringBuilder builder = new StringBuilder();
		builder.append(setTabs(2));
		builder.append("} catch (SecurityException e) {");
		builder.append(lineSeparator);
		return builder;
	}
	
	private StringBuilder setCatchReturn() {
		StringBuilder builder = new StringBuilder();
		builder.append(setTabs(3));
		builder.append("return defaultString;");
		builder.append(lineSeparator);
		return builder;
	}
	
	private StringBuilder end(int tabCount) {
		StringBuilder builder = new StringBuilder();
		builder.append(setTabs(tabCount));
		builder.append("}");
		builder.append(lineSeparator);
		return builder;
	}
	
	private StringBuilder setTabs(int tabCount) {
		StringBuilder builder = new StringBuilder();
		for (int i = 0; i < tabCount; i++) {
			builder.append("\t");
		}
		return builder;
	}
	
	private StringBuilder convertString(String s) {
		StringBuilder builder = new StringBuilder();
		boolean isCapitalized = false;
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			if (isCapitalized) {
				builder.append(Character.toUpperCase(c));
				isCapitalized = false;
			} else if (c == '.') {
				isCapitalized = true;
			} else {
				builder.append(c);
			}
		}
		return builder;
	}
	
	private void writeClass(String s) {
		String path = "C:/Eclipse/eclipse-4.2-work/" + 
				"com.ggl.systemproperty/src/com/ggl" + 
				"/systemproperty/SystemProperty.java";
		try {
			BufferedWriter writer = 
					new BufferedWriter(new FileWriter(path));
			writer.write(s);
			writer.flush();
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	
	public static void main(String[] args) {
		new CreatePropertyClass().run();
	}

}

Is that enough StringBuilder instances for you?

You can use the setTabs method and the convertString method in any Java application you write that writes a Java class. The convertString method takes a string like “line.separator” and camel cases it to “lineSeparator”. Dots aren’t valid in Java field names, so they are removed.

If you study the code in the CreatePropertyClass, you’ll notice that all of the constant string methods are similar and repetitive.

You know, it would be interesting to write a Java application that would read a Java class template, and generate the StringBuilder code necessary to create a Java application that would create a Java class.

Wow. My mind is blown at how META that would be.

Post a Comment

Your email is kept private. Required fields are marked *