package org.hamcrest.generator; import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * API for syntactic sugar and wrapper code generation framework. * <p>Generally, Matcher factory methods are defined all over the place, which makes it * really hard to remember which methods need to be imported and doesn't allow you * to easily browse a list of all of them. * <p>This generates one uber matcher factory that delegates to all the respective classes. * <p>Additionally, this allows the uber-classes to do things like wrap matchers in adapters * (e.g. to make them EasyMock friendly) or even generate wrappers in other languages * (such as JRuby or Jython). * <p>You need to add at least one writer and at least one factory method for this to be * any help. * <h3>Usage</h3> * <pre> * SugarGenerator sugarGenerator = new SugarGenerator(); * try { * // Write out sugar as standard Hamcrest factories. * sugarGenerator.addWriter( * new HamcrestFactoryWriter("com.some.package", "MyMatcherClass", new FileWriter(...))); * // Also write out sugar as EasyMock compatible factories. * sugarGenerator.addWriter( * new EasyMockFactoryWriter("com.some.package", "MyEasyMatcherClass", new FileWriter(...))); * // Add a load of Matcher factories to the generated sugar. The factory methods * // are read via reflection. * sugarGenerator.addFactoryMethods(new ReflectiveFactoryReader(IsIn.class)); * sugarGenerator.addFactoryMethods(new ReflectiveFactoryReader(IsSame.class)); * sugarGenerator.addFactoryMethods(new ReflectiveFactoryReader(IsNot.class)); * // Generate everything! * sugarGenerator.generate(); * } finally { * // Clean up... close all streams. * sugarGenerator.close(); * }</pre> * * @author Joe Walnes * @see FactoryWriter * @see HamcrestFactoryWriter * @see ReflectiveFactoryReader */ public class SugarGenerator implements Closeable, SugarConfiguration { private final List<FactoryWriter> factoryWriters = new ArrayList<FactoryWriter>(); private final List<FactoryMethod> factoryMethods = new ArrayList<FactoryMethod>(); /** * Add a writer of factories. */ @Override public void addWriter(FactoryWriter factoryWriter) { factoryWriters.add(factoryWriter); } /** * Add a FactoryMethod that will be generated in the sugar. * * @see ReflectiveFactoryReader * @see FactoryMethod */ @Override public void addFactoryMethod(FactoryMethod method) { factoryMethods.add(method); } /** * Convenient way to add multiple FactoryMethods. * * @see #addFactoryMethod(FactoryMethod) */ @Override public void addFactoryMethods(Iterable<FactoryMethod> methods) { for (FactoryMethod method : methods) { addFactoryMethod(method); } } /** * Generate all the factory methods using all the writers. * * This should always happen AFTER adding factory methods and writers. */ public void generate() throws IOException { for (FactoryWriter factoryWriter : factoryWriters) { factoryWriter.writeHeader(); for (FactoryMethod factoryMethod : factoryMethods) { factoryWriter.writeMethod(factoryMethod.getName(), factoryMethod); } factoryWriter.writeFooter(); factoryWriter.flush(); } } /** * Close all underlying streams. Calling this means you don't have to explicitly * keep track of all the File streams and close them. */ @Override public void close() throws IOException { for (FactoryWriter factoryWriter : factoryWriters) { factoryWriter.close(); } } }