Archive for February, 2010

Dummy Files in Unit Tests

Monday, February 15th, 2010

Let’s say that you have an application which accepts a filepath as part of its interface. When unit testing, you may have some dummy files which you use to test the interface.

However, getting the full path of these test files can be a pain.

Assuming that the files are (as per Maven conventions) in /src/test/resources/ then they will be put on the classpath (by default /test-classes/ as part of the build process.

If you’re using Spring, then you can take advantage of that to do the following:

Resource classPathResource = new ClassPathResource("/simple_dataset.csv");
String inputFilename = classPathResource.getURI().getPath();

inputFilename now contains the full path to the file, and can be passed into your application as per normal.

Load a CSV file into an ArrayList

Monday, February 15th, 2010

It isn’t hard to write code which iterates through files, cuts them up and loads them into an ArrayList. And if the delimiting character isn’t a comma? No problem, it isn’t hard to add that functionality. And if you want to skip the first set of lines because you’re paginating the file? Easy! And if you want to write some changes back out again – no problem, I can add that feature in no time.

Except it’s already been done. So why waste the time? :-)

OpenCSV is an easy to use library which you can embed into your applications (Apache License). Then you can simply do something like:

// Loop through file
List<String[]> inputSet = new ArrayList<String[]>();
try {
   CSVReader reader = new CSVReader(new FileReader(inputFilename));
   String[] nextLine;
   try {
	while ((nextLine = reader.readNext()) != null) {
   	  inputSet.add(nextLine);
	}
   } catch (IOException e) {
      // WHATEVER
   }
} catch (FileNotFoundException e) {
   // WHATEVER
}

And you’re done. See the OpenCSV homepage for some of the other features.

Custom XStream Converter

Saturday, February 13th, 2010

XStream is a great utility for serializing objects to XML – and then back again.

The default XML output is neat enough – albeit a little verbose (using fully qualified class names for example). There are plenty of tutorials available showing how to use XStream aliases to clean up the output (e.g. Manual Tweaking Output).

However, sometimes it is not enough to just tweak and alias – more serious manipulation of the output stream is required. For example, complicated Collection-based objects come out very cumbersome (but generic – which is the point). Luckily, XStream provides a powerful interface for custom generation (A Converter in XStream-speak).

To fully control how an object is serialised, create a new class which implements the Converter interface:

import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

public class MyCustomMapConverter implements Converter {

	@Override
	public void marshal(Object source, HierarchicalStreamWriter writer,
			MarshallingContext context) {
          // TODO
	}

	@Override
	public boolean canConvert(Class clazz) {
		return clazz.equals(ClassToBeConverted.class);
	}

        @Override
	public Object unmarshal(HierarchicalStreamReader reader,
			UnmarshallingContext context) {
          // TODO
	}

}

As can be seen above, the canConvert method tells XStream which objects should use this converter. The other two methods are called when converting objects to XML (marshal) or converting XML to objects (unmarshal).

Marshalling the object is achieved by starting and ending Nodes, while populating their attributes and values. So for example:

@Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
  for (RouterConfigRuleMap routerConfigRuleMap : routerConfigRuleMapSet.getRouterConfigRuleMapSet())
  {
	writer.startNode("routetype");
	writer.addAttribute("type", routerConfigRuleMap.getRouteType().toString());
	writer.endNode();
}

will iterate through a collection generating the following output:

<routetype type="example" />
<routetype type="foo" />

These patterns can become arbitrarily complex.

Unmarshalling is a little more complicated. XStream will work out which (registered – see later) Converter to use by matching the node to the class (in my head these seems like an extremely difficult task… I’m not sure how XStream has implemented this – maybe I should look into this!) The node (and all children) will be passed to the unmarshal method.

It is up to us to define how the target object is populated from these nodes. Methods are provided to:

  • Check if the node has children (reader.hasMoreChildren())
  • Move down to the child level (reader.moveDown())
  • Move back up to the parent level (reader.moveUp())

The moveUp/Down methods can be called as many times as necessary to walk the tree.

I provide a full example as I couldn’t find many good ones on the net:

@Override
public Object unmarshal(HierarchicalStreamReader reader,	UnmarshallingContext context) {

  RouterConfigRuleMapSet routerConfigRuleMapSet = new RouterConfigRuleMapSet();

  while (reader.hasMoreChildren()) {
	reader.moveDown();

	RouterConfigRuleMap routerConfigRuleMap = new RouterConfigRuleMap();
	routerConfigRuleMap.setRouteType(RouteType.valueOf(reader.getAttribute("type")));

	while (reader.hasMoreChildren()) {
		reader.moveDown();
		RouterConfigRule routerConfigRule = new RouterConfigRule();

		if (reader.getAttribute("extractor") != null) {
			routerConfigRule.setExtractor(reader.getAttribute("extractor"));
		}

		if (reader.getAttribute("formatter") != null) {
			routerConfigRule.setFormatter(reader.getAttribute("formatter"));
		}

		if (reader.getAttribute("distributor") != null) {
			routerConfigRule.setDistributor(reader.getAttribute("distributor"));
		}

		routerConfigRuleMap.putRouterConfigRule(reader.getAttribute("name"), routerConfigRule);

		reader.moveUp();
		}
	reader.moveUp();
	routerConfigRuleMapSet.addRouterConfigRuleMapSet(routerConfigRuleMap);
	}
  return routerConfigRuleMapSet;
}

Out of completeness, here is the equivalent marshal implementation:

@Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {

  RouterConfigRuleMapSet routerConfigRuleMapSet = (RouterConfigRuleMapSet)source;
  for (RouterConfigRuleMap routerConfigRuleMap : routerConfigRuleMapSet.getRouterConfigRuleMapSet())
  {
	writer.startNode("routetype");
	writer.addAttribute("type", routerConfigRuleMap.getRouteType().toString());

	for (Object routerConfig : routerConfigRuleMap.routerConfigRuleMap.entrySet()) {
		Entry<String, RouterConfigRule> entry = (Entry<String, RouterConfigRule>) routerConfig;
		RouterConfigRule routerConfigRule = entry.getValue(); 

		writer.startNode("routename");
		writer.addAttribute("name", entry.getKey().toString());
		if (routerConfigRule.getExtractor() != null) {
			writer.addAttribute("extractor", routerConfigRule.getExtractor());
		}

		if (routerConfigRule.getFormatter() != null) {
			writer.addAttribute("formatter", routerConfigRule.getFormatter());
		}

		if (routerConfigRule.getDistributor() != null) {
			writer.addAttribute("distributor", routerConfigRule.getDistributor());
		}

		writer.endNode();
	}
	writer.endNode();
  }
}

To register the Converter simply add the following line when you instantiate the XStream object:

XStream stream = new XStream();
  stream.registerConverter(new RouterConfigMapConverter()); // NEW LINE