SourceExporter takes a Java object that has been instaniated and set in a particular state and produces the source code necessary to recreate the object in that state. The object and all of its state must contain only JavaBeans, Lists, Maps, Sets, 1D and 2D arrays of primitives or Objects, or wrapper classes of primitive Objects in order to use the default settings. If required classes are missing public accessors, are immutable, or represent an unsupported collection type, custom marshallers can be added to the marshaller chain. There are examples provided for these (see two sections below).
SourceExporter is intended to be used in cases where traditional functional testing is difficult. A button or link in an application can be set to run SourceExporter, which produces a Java source file that can be used for testing that feature. In this way, the GUI can be used to set up the required objects, and then the source for creating objects in that state can be automatically generated. This source code can then be used by an automated test suite, such as one based on the JUnit framework. Testing can now proceed without using a testing tool to directly manipulate the GUI: such tools tend to be fragile in the author's opinion, either halting seemly at random during playback and/or requiring frequent re-recording.
To create a marshaller that exports source code for an immutable class, see CustomMarshallerExample. To create a marshaller that exports source code for a collection, see ListMarshaller. To add a new marshaller to the chain, see CustomSourceExporter.
SourceExporter can be run in a Java 1.4+ environment and produces valid Java 1.4 code, including source code for boxing and unboxing if necessary. A single jar that has the the two dependencies (SourceGen and FactoryGen lib) included is available and can also be built with ant from source if desired.
See the JavaDoc.
The following example code is provided as a starting point to see how SourceExporter works, and what the code that uses it looks like.
package ca.quine.jcommons.sourceexporter.test; import java.io.File; import java.util.ArrayList; import java.util.List; import ca.quine.jcommons.sourceexporter.SourceExporter; import junitx.framework.FileAssert; public class ListExportTest extends SourceExporterTestCase { public void testListOfString() throws Exception { SourceExporter sourceExporter = new SourceExporter(); File tempFile = File.createTempFile("Source-Export-List-Of-String", ".java"); tempFile.deleteOnExit(); List listOfString = new ArrayList(); listOfString.add("1"); listOfString.add("2"); listOfString.add("3"); sourceExporter.export(tempFile.getAbsolutePath(), RESOURCES_PACKAGE, "CreateListOfString", "java.util.List", "createList", listOfString); FileAssert.assertEquals(new File(TEST_DIR + "CreateListOfString.java"), tempFile); } public void testListOfInt() throws Exception { SourceExporter sourceExporter = new SourceExporter(); File tempFile = File.createTempFile("Source-Export-List-Of-Int", ".java"); tempFile.deleteOnExit(); List listOfInt = new ArrayList(); listOfInt.add(new Integer(1)); listOfInt.add(new Integer(2)); listOfInt.add(new Integer(3)); sourceExporter.export(tempFile.getAbsolutePath(), RESOURCES_PACKAGE, "CreateListOfInt", "java.util.List", "createList", listOfInt); FileAssert.assertEquals(new File(TEST_DIR + "CreateListOfInt.java"), tempFile); } public void testListOfNodes() throws Exception { SourceExporter sourceExporter = new SourceExporter(); File tempFile = File.createTempFile("Source-Export-List-Of-Nodes", ".java"); tempFile.deleteOnExit(); List listOfString = new ArrayList(); listOfString.add(new Node("First Node")); listOfString.add(new Node("Second Node")); listOfString.add(new Node("Third Node")); sourceExporter.export(tempFile.getAbsolutePath(), RESOURCES_PACKAGE, "CreateListOfNodes", "java.util.List", "createList", listOfString); FileAssert.assertEquals(new File(TEST_DIR + "CreateListOfNodes.java"), tempFile); } }
The source code above produces three files, one to create a List of Strings, one to create a List of ints, and one to create a List of Nodes. The latter is shown below. The default indenting is 4 spaces, Windows/DOS style line breaks and 80 character lines (for method signatures: individual source lines may exceed 80 characters). These defaults can be changed if desired. In order to change the defaults, construct a MarshallingContext and set the appropriate properties on the FormattingRules property. Pass this MarshallingContext into the SourceExporter.export method when exporting the source code.
package ca.quine.jcommons.sourceexporter.test.resources; import java.util.HashMap; import java.util.Map; public class CreateListOfNodes { public ca.quine.jcommons.sourceexporter.test.Node createNode2( Map alreadySeenMap) { ca.quine.jcommons.sourceexporter.test.Node retVal = new ca.quine.jcommons.sourceexporter.test.Node(); alreadySeenMap.put(new Integer(2), retVal); retVal.setNodeName("First Node"); return retVal; } public ca.quine.jcommons.sourceexporter.test.Node createNode3( Map alreadySeenMap) { ca.quine.jcommons.sourceexporter.test.Node retVal = new ca.quine.jcommons.sourceexporter.test.Node(); alreadySeenMap.put(new Integer(3), retVal); retVal.setNodeName("Second Node"); return retVal; } public ca.quine.jcommons.sourceexporter.test.Node createNode4( Map alreadySeenMap) { ca.quine.jcommons.sourceexporter.test.Node retVal = new ca.quine.jcommons.sourceexporter.test.Node(); alreadySeenMap.put(new Integer(4), retVal); retVal.setNodeName("Third Node"); return retVal; } public java.util.ArrayList createArrayList1(Map alreadySeenMap) { java.util.ArrayList retVal = new java.util.ArrayList(); alreadySeenMap.put(new Integer(1), retVal); retVal.add(createNode2(alreadySeenMap)); retVal.add(createNode3(alreadySeenMap)); retVal.add(createNode4(alreadySeenMap)); return retVal; } public java.util.List createList() { Map alreadySeenMap = new HashMap(); return createArrayList1(alreadySeenMap); } }