Skip to content

Commit 64218a6

Browse files
author
joeretro
committed
Syntactic sugar and wrapper code generation framework.
Currently 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. This generates one uber matcher factory that delegates to all the respective classes. Additionally, this allows the uber-classes to do things like wrap matchers in adaptors (e.g. to make them EasyMock friendly). This also allows users to group their own collections of Matchers together in a way that makes sense to them.
1 parent aadc4d4 commit 64218a6

12 files changed

Lines changed: 1039 additions & 6 deletions

File tree

CHANGES.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
* Constraint renamed to Matcher.
88
* Constraint.eval() renamed to Matcher.match().
99
* Packages moved from org.jmock.core.matcher to org.hamcrest.
10-
* Merged SelfDescribing into Matcher interface.
1110
* SelfDescribing.describe() no longer returns a StringBuffer. Simpler for users to implement.
1211
* Introduced Description interface that acts as a domain centric abstraction over StringBuffer.
1312

@@ -23,6 +22,7 @@
2322

2423
[Integration Changes]
2524
* Added MatcherAssert, for assertThat(Object, Matcher) in JUnit.
25+
* Added syntactic sugar generator.
2626

2727
[Misc]
2828
* Split code into different JARs

README.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ The complete source for Ham Crest is bundled. This includes:
4545
* Matcher API [src/api]
4646
* Matcher libary [src/library]
4747
* Matcher integrations [src/integration]
48+
* Syntactic sugar generator [src/generator]
4849
* Unit tests [src/unit-test]
4950
* Ant build file [build.xml]
5051
* Dependencies [lib]

build.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
<target name="build" description="Build project">
1313
<mkdir dir="build"/>
1414
<java-to-jar srcdir="src/api" destjar="build/hamcrest-api-${version}.jar"/>
15+
<java-to-jar srcdir="src/generator" destjar="build/hamcrest-generator-${version}.jar"
16+
classpath="build/hamcrest-api-${version}.jar"/>
1517
<java-to-jar srcdir="src/library" destjar="build/hamcrest-library-${version}.jar"
1618
classpath="build/hamcrest-api-${version}.jar"/>
1719
<java-to-jar srcdir="src/integration" destjar="build/hamcrest-integration-${version}.jar"
1820
classpath="build/hamcrest-api-${version}.jar;build/hamcrest-library-${version}.jar"/>
1921
<java-to-jar srcdir="src/unit-test" destjar="build/hamcrest-unit-test-${version}.jar"
20-
classpath="build/hamcrest-api-${version}.jar;build/hamcrest-library-${version}.jar;build/hamcrest-integration-${version}.jar"/>
22+
classpath="build/hamcrest-api-${version}.jar;build/hamcrest-library-${version}.jar;build/hamcrest-integration-${version}.jar;build/hamcrest-generator-${version}.jar"/>
2123
<java-to-jar srcdir="src/examples" destjar="build/hamcrest-examples-${version}.jar"
2224
classpath="build/hamcrest-api-${version}.jar;build/hamcrest-library-${version}.jar;build/hamcrest-integration-${version}.jar"/>
2325
</target>

src/api/org/hamcrest/Factory.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.hamcrest;
2+
3+
import static java.lang.annotation.ElementType.METHOD;
4+
import java.lang.annotation.Retention;
5+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
6+
import java.lang.annotation.Target;
7+
8+
/**
9+
* Marks a Hamcrest static factory method so tools recognise them.
10+
* A factory method is an equivalent to a named constructor.
11+
*
12+
* @author Joe Walnes
13+
*/
14+
@Retention(RUNTIME)
15+
@Target({METHOD})
16+
public @interface Factory {
17+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package org.hamcrest.generator;
2+
3+
4+
import java.util.ArrayList;
5+
import static java.util.Collections.unmodifiableList;
6+
import java.util.List;
7+
8+
/**
9+
* Represents a Matcher Factory method.
10+
*
11+
* <p>This class uses Strings to represent things instead of java.lang.reflect equivalents,
12+
* allowing methods to be defined from sources other than reflection of classes in the
13+
* classpath.
14+
*
15+
* @author Joe Walnes
16+
* @see SugarGenerator
17+
* @see org.hamcrest.Factory
18+
*/
19+
public class FactoryMethod {
20+
21+
private final String matcherClass;
22+
private final String factoryMethod;
23+
24+
private String generifiedType;
25+
private List<Parameter> parameters = new ArrayList<Parameter>();
26+
private List<String> exceptions = new ArrayList<String>();
27+
private List<String> genericTypeParameters = new ArrayList<String>();
28+
private String javaDoc;
29+
30+
public FactoryMethod(String matcherClass, String factoryMethod) {
31+
this.matcherClass = matcherClass;
32+
this.factoryMethod = factoryMethod;
33+
}
34+
35+
/**
36+
* Original class this factory method came from.
37+
*/
38+
public String getMatcherClass() {
39+
return matcherClass;
40+
}
41+
42+
/**
43+
* Original name of factory method.
44+
*/
45+
public String getName() {
46+
return factoryMethod;
47+
}
48+
49+
public void setGenerifiedType(String generifiedType) {
50+
this.generifiedType = generifiedType;
51+
}
52+
53+
/**
54+
* Generified type of matcher.
55+
* ie. 'public Matcher&lt;THISBIT&gt; blah(...)'
56+
*/
57+
public String getGenerifiedType() {
58+
return generifiedType;
59+
}
60+
61+
public void addParameter(String type, String name) {
62+
parameters.add(new Parameter(type, name));
63+
}
64+
65+
/**
66+
* List of Parameters passed to factory method.
67+
* ie. 'public Matcher&lt;...&ht;> blah(THIS, AND, THAT)'
68+
*/
69+
public List<Parameter> getParameters() {
70+
return unmodifiableList(parameters);
71+
}
72+
73+
public void addException(String exception) {
74+
exceptions.add(exception);
75+
}
76+
77+
/**
78+
* List of exceptions thrown by factory method.
79+
* ie. 'public Matcher&lt;...&gt; blah(...) throws THIS, THAT'
80+
*/
81+
public List<String> getExceptions() {
82+
return unmodifiableList(exceptions);
83+
}
84+
85+
public void addGenericTypeParameter(String genericTypeParameter) {
86+
genericTypeParameters.add(genericTypeParameter);
87+
}
88+
89+
/**
90+
* List of generic type parameters for factory method definition.
91+
* ie. 'public &lt;THIS,THAT&gt; Matcher&lt;...&gt; blah(...)'
92+
*
93+
* @return
94+
*/
95+
public List<String> getGenericTypeParameters() {
96+
return unmodifiableList(genericTypeParameters);
97+
}
98+
99+
public void setJavaDoc(String javaDoc) {
100+
this.javaDoc = javaDoc;
101+
}
102+
103+
/**
104+
* JavaDoc definition of factory method.
105+
* Excludes surrounding comment markers.
106+
* Note that using standard Java reflection it is not possible to obtain this,
107+
* however source code parsers can read this.
108+
*/
109+
public String getJavaDoc() {
110+
return javaDoc;
111+
}
112+
113+
/**
114+
* Represents a parameter passed to a factory method.
115+
*
116+
* @see FactoryMethod
117+
*/
118+
public static class Parameter {
119+
120+
private final String type;
121+
private final String name;
122+
123+
public Parameter(String type, String name) {
124+
this.type = type;
125+
this.name = name;
126+
}
127+
128+
/**
129+
* Type of parameter, including any generic declarations.
130+
*/
131+
public String getType() {
132+
return type;
133+
}
134+
135+
/**
136+
* Name of parameter, if it can be obtained. If it cannot
137+
* be obtained, a sensible default will returned instead.
138+
*/
139+
public String getName() {
140+
return name;
141+
}
142+
143+
}
144+
145+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package org.hamcrest.generator;
2+
3+
import java.io.Closeable;
4+
import java.io.Flushable;
5+
import java.io.IOException;
6+
7+
/**
8+
* Writes syntactic sugar code for factories.
9+
* <p>Implementations of this could include vanilla factory methods for
10+
* Hamcrest matchers, wrapped factories for other libraries or factories
11+
* in other languages (jython, jruby, groovy, etc).
12+
* <h3>Usage:</h3>
13+
* <pre>
14+
* writer.writeHeader(...);
15+
* <p/>
16+
* writer.writeMethod(...);
17+
* writer.writeMethod(...);
18+
* writer.writeMethod(...);
19+
* ...
20+
* writer.writeFooter(...);
21+
* writer.close();
22+
* </pre>
23+
*
24+
* @author Joe Walnes
25+
* @see FactoryMethod
26+
* @see SugarGenerator
27+
* @see HamcrestFactoryWriter
28+
*/
29+
public interface FactoryWriter extends Closeable, Flushable {
30+
31+
/**
32+
* Write the code header.
33+
*/
34+
void writeHeader() throws IOException;
35+
36+
/**
37+
* Writes code that delegates to a method.
38+
*
39+
* @param generatedMethodName
40+
* @param factoryMethodToDelegateTo
41+
*/
42+
void writeMethod(String generatedMethodName, FactoryMethod factoryMethodToDelegateTo) throws IOException;
43+
44+
/**
45+
* Write any necessary code to finish the output.
46+
*/
47+
void writeFooter() throws IOException;
48+
49+
}

0 commit comments

Comments
 (0)