001: package com.mockrunner.gen.proc;
002:
003: import java.lang.reflect.Constructor;
004: import java.lang.reflect.Method;
005: import java.lang.reflect.Modifier;
006: import java.util.ArrayList;
007: import java.util.List;
008:
009: import com.mockrunner.base.BaseTestCase;
010: import com.mockrunner.base.HTMLOutputModule;
011: import com.mockrunner.base.HTMLOutputTestCase;
012: import com.mockrunner.gen.proc.JavaClassGenerator.ConstructorDeclaration;
013: import com.mockrunner.gen.proc.JavaClassGenerator.MethodDeclaration;
014: import com.mockrunner.util.common.ArrayUtil;
015: import com.mockrunner.util.common.ClassUtil;
016:
017: public class StandardAdapterProcessor implements AdapterProcessor {
018: private String name;
019: private String output;
020:
021: public void process(Class module, List excludedMethods) {
022: JavaClassGenerator classGenerator = new JavaClassGenerator();
023: BCELClassAnalyzer analyzer = new BCELClassAnalyzer(module);
024: classGenerator.setCreateJavaDocComments(true);
025: classGenerator.setPackage(module.getPackage());
026: String className = getClassNameFromBaseName(getBaseName(module));
027: name = getJavaFileName(module, className);
028: classGenerator.setClassName(className);
029: classGenerator.setAbstract(true);
030: Class super Class = getSuperClass(module);
031: if (null != super Class) {
032: classGenerator.setSuperClass(getSuperClass(module));
033: }
034: classGenerator.setClassComment(getClassComment(module));
035: Class factoryClass = determineFactoryClass(module);
036: MemberInfo memberInfo = new MemberInfo();
037: memberInfo.setModule(module);
038: memberInfo.setFactory(factoryClass);
039: addMemberDeclarations(classGenerator, memberInfo);
040: addConstructors(classGenerator);
041: addTearDownMethod(classGenerator, memberInfo);
042: addSetUpMethod(classGenerator, memberInfo);
043: addAdditionalControlMethods(classGenerator, memberInfo);
044: addDelegatorMethods(classGenerator, analyzer, excludedMethods,
045: memberInfo);
046: output = classGenerator.generate();
047: }
048:
049: public String getName() {
050: return name;
051: }
052:
053: public String getOutput() {
054: return output;
055: }
056:
057: private Class determineFactoryClass(Class module) {
058: Constructor[] constructors = module.getDeclaredConstructors();
059: for (int ii = 0; ii < constructors.length; ii++) {
060: Constructor constructor = constructors[ii];
061: if (constructor.getParameterTypes().length == 1) {
062: return constructor.getParameterTypes()[0];
063: }
064: }
065: throw new RuntimeException(
066: "Module "
067: + module.getName()
068: + " has no constructor with mock object factory argument");
069: }
070:
071: private void addTearDownMethod(JavaClassGenerator classGenerator,
072: MemberInfo memberInfo) {
073: MethodDeclaration method = createProtectedMethod();
074: method.setName("tearDown");
075: method.setExceptions(new Class[] { Exception.class });
076: method.setCodeLines(getTearDownMethodCodeLines(memberInfo));
077: classGenerator.addMethodDeclaration(method);
078: }
079:
080: private void addSetUpMethod(JavaClassGenerator classGenerator,
081: MemberInfo memberInfo) {
082: MethodDeclaration method = createProtectedMethod();
083: method.setName("setUp");
084: method.setExceptions(new Class[] { Exception.class });
085: String[] comment = new String[2];
086: comment[0] = "Creates the "
087: + getJavaDocLink(memberInfo.getModule()) + ". If you";
088: comment[1] = "overwrite this method, you must call <code>super.setUp()</code>.";
089: method.setCommentLines(comment);
090: method.setCodeLines(getSetUpMethodCodeLines(memberInfo));
091: classGenerator.addMethodDeclaration(method);
092: }
093:
094: private void addHTMLOutputMethods(
095: JavaClassGenerator classGenerator, MemberInfo memberInfo) {
096: Class module = memberInfo.getModule();
097: if (!HTMLOutputModule.class.isAssignableFrom(module))
098: return;
099: MethodDeclaration htmlOutputMethod = createProtectedMethod();
100: htmlOutputMethod.setName("getHTMLOutputModule");
101: htmlOutputMethod.setReturnType(HTMLOutputModule.class);
102: String[] comment = new String[3];
103: comment[0] = "Returns the " + getJavaDocLink(module) + " as";
104: comment[1] = getJavaDocLink(HTMLOutputModule.class) + ".";
105: comment[2] = "@return the "
106: + getJavaDocLink(HTMLOutputModule.class);
107: htmlOutputMethod.setCommentLines(comment);
108: String[] codeLines = new String[] { "return "
109: + memberInfo.getModuleMember() + ";" };
110: htmlOutputMethod.setCodeLines(codeLines);
111: classGenerator.addMethodDeclaration(htmlOutputMethod);
112: }
113:
114: private void addDelegatorMethods(JavaClassGenerator classGenerator,
115: BCELClassAnalyzer analyzer, List excludedMethods,
116: MemberInfo memberInfo) {
117: Method[] moduleMethods = getDelegateMethods(memberInfo
118: .getModule(), excludedMethods);
119: for (int ii = 0; ii < moduleMethods.length; ii++) {
120: Method method = moduleMethods[ii];
121: MethodDeclaration delegationMethod = createProtectedMethod();
122: delegationMethod.setName(method.getName());
123: delegationMethod.setReturnType(method.getReturnType());
124: Class[] exceptions = method.getExceptionTypes();
125: if (exceptions.length > 0) {
126: delegationMethod.setExceptions(exceptions);
127: }
128: Class[] parameters = method.getParameterTypes();
129: String[] argumentNames = null;
130: if (parameters.length > 0) {
131: delegationMethod.setArguments(parameters);
132: argumentNames = analyzer.getArgumentNames(method);
133: if (null == argumentNames || argumentNames.length <= 0) {
134: argumentNames = prepareSuitableArgumentNames(parameters);
135: }
136: delegationMethod.setArgumentNames(argumentNames);
137: }
138: String delegationCodeLine = createDelegationCodeLine(
139: method, memberInfo.getModuleMember(), argumentNames);
140: delegationMethod
141: .setCodeLines(new String[] { delegationCodeLine });
142: String[] delegationMethodComment = createDelegationMethodComment(
143: analyzer, memberInfo.getModule(), method);
144: delegationMethod.setCommentLines(delegationMethodComment);
145: classGenerator.addMethodDeclaration(delegationMethod);
146: }
147: }
148:
149: private void addConstructors(JavaClassGenerator classGenerator) {
150: classGenerator.addConstructorDeclaration();
151: ConstructorDeclaration constructor = new ConstructorDeclaration();
152: constructor.setArguments(new Class[] { String.class });
153: constructor.setArgumentNames(new String[] { "name" });
154: constructor.setCodeLines(new String[] { "super(name);" });
155: classGenerator.addConstructorDeclaration(constructor);
156: }
157:
158: private String[] prepareSuitableArgumentNames(Class[] arguments) {
159: String[] names = new String[arguments.length];
160: for (int ii = 0; ii < arguments.length; ii++) {
161: names[ii] = ClassUtil.getArgumentName(arguments[ii]);
162: }
163: ArrayUtil.ensureUnique(names);
164: return names;
165: }
166:
167: private Method[] getDelegateMethods(Class module,
168: List excludedMethods) {
169: Method[] moduleMethods = module.getDeclaredMethods();
170: List delegateMethods = new ArrayList();
171: for (int ii = 0; ii < moduleMethods.length; ii++) {
172: Method currentMethod = moduleMethods[ii];
173: if (shouldMethodBeAdded(currentMethod, excludedMethods)) {
174: delegateMethods.add(currentMethod);
175: }
176: }
177: return (Method[]) delegateMethods
178: .toArray(new Method[delegateMethods.size()]);
179: }
180:
181: private boolean shouldMethodBeAdded(Method currentMethod,
182: List excludedMethods) {
183: if (!Modifier.isPublic(currentMethod.getModifiers()))
184: return false;
185: if (null == excludedMethods)
186: return true;
187: if (excludedMethods.contains(currentMethod.getName()))
188: return false;
189: return true;
190: }
191:
192: private String[] createDelegationMethodComment(
193: BCELClassAnalyzer analyzer, Class module, Method method) {
194: StringBuffer buffer = new StringBuffer();
195: buffer.append("Delegates to {@link ");
196: buffer.append(module.getName());
197: buffer.append("#");
198: buffer.append(method.getName());
199: Class[] argumentTypes = method.getParameterTypes();
200: if (argumentTypes.length > 0) {
201: buffer.append("(");
202: for (int ii = 0; ii < argumentTypes.length; ii++) {
203: buffer
204: .append(ClassUtil
205: .getClassName(argumentTypes[ii]));
206: if (ii < argumentTypes.length - 1) {
207: buffer.append(", ");
208: }
209: }
210: buffer.append(")");
211: }
212: buffer.append("}");
213: if (analyzer.isMethodDeprecated(method)) {
214: return new String[] { buffer.toString(), "@deprecated" };
215: }
216: return new String[] { buffer.toString() };
217: }
218:
219: private String createDelegationCodeLine(Method method,
220: String memberName, String[] argumentNames) {
221: StringBuffer buffer = new StringBuffer();
222: if (!Void.TYPE.equals(method.getReturnType())) {
223: buffer.append("return ");
224: }
225: buffer.append(memberName);
226: buffer.append(".");
227: buffer.append(method.getName());
228: buffer.append("(");
229: if (null != argumentNames) {
230: for (int ii = 0; ii < argumentNames.length; ii++) {
231: buffer.append(argumentNames[ii]);
232: if (ii < argumentNames.length - 1) {
233: buffer.append(", ");
234: }
235: }
236: }
237: buffer.append(");");
238: return buffer.toString();
239: }
240:
241: protected Class[] getAdditionalImports() {
242: return new Class[0];
243: }
244:
245: protected String getBaseName(Class module) {
246: String name = ClassUtil.getClassName(module);
247: int moduleIndex = name.indexOf("Module");
248: if (moduleIndex > -1) {
249: name = name.substring(0, moduleIndex);
250: }
251: return name;
252: }
253:
254: protected void addGetAndSetMethodPair(
255: JavaClassGenerator classGenerator, Class clazz,
256: String memberName) {
257: addGetMethod(classGenerator, clazz, memberName);
258: addSetMethod(classGenerator, clazz, memberName);
259: }
260:
261: protected void addGetMethod(JavaClassGenerator classGenerator,
262: Class clazz, String memberName) {
263: addGetMethod(classGenerator, clazz, new String[] { "return "
264: + memberName + ";" });
265: }
266:
267: protected void addGetMethod(JavaClassGenerator classGenerator,
268: Class clazz, String[] codeLines) {
269: MethodDeclaration getMethod = createProtectedMethod();
270: getMethod.setName("get" + ClassUtil.getClassName(clazz));
271: getMethod.setReturnType(clazz);
272: String[] comment = new String[2];
273: comment[0] = "Gets the " + getJavaDocLink(clazz) + ".";
274: comment[1] = "@return the " + getJavaDocLink(clazz);
275: getMethod.setCommentLines(comment);
276: getMethod.setCodeLines(codeLines);
277: classGenerator.addMethodDeclaration(getMethod);
278: }
279:
280: protected void addSetMethod(JavaClassGenerator classGenerator,
281: Class clazz, String memberName) {
282: String[] comment;
283: MethodDeclaration setMethod = createProtectedMethod();
284: setMethod.setName("set" + ClassUtil.getClassName(clazz));
285: comment = new String[2];
286: comment[0] = "Sets the " + getJavaDocLink(clazz) + ".";
287: comment[1] = "@param " + memberName + " the "
288: + getJavaDocLink(clazz);
289: setMethod.setCommentLines(comment);
290: setMethod.setArguments(new Class[] { clazz });
291: setMethod.setArgumentNames(new String[] { memberName });
292: setMethod.setCodeLines(new String[] { "this." + memberName
293: + " = " + memberName + ";" });
294: classGenerator.addMethodDeclaration(setMethod);
295: }
296:
297: protected String getJavaDocLink(Class clazz) {
298: return "{@link " + clazz.getName() + "}";
299: }
300:
301: protected MethodDeclaration createProtectedMethod() {
302: MethodDeclaration method = new MethodDeclaration();
303: method.setModifier(Modifier.PROTECTED);
304: return method;
305: }
306:
307: protected void addMemberDeclarations(
308: JavaClassGenerator classGenerator, MemberInfo memberInfo) {
309: String memberName = ClassUtil.getArgumentName(memberInfo
310: .getModule());
311: memberInfo.setModuleMember(memberName);
312: classGenerator.addMemberDeclaration(memberInfo.getModule(),
313: memberName);
314: }
315:
316: protected String[] getSetUpMethodCodeLines(MemberInfo memberInfo) {
317: String[] codeLines = new String[2];
318: codeLines[0] = "super.setUp();";
319: String factoryCall = "get"
320: + ClassUtil.getClassName(memberInfo.getFactory());
321: codeLines[1] = memberInfo.getModuleMember() + " = create"
322: + ClassUtil.getClassName(memberInfo.getModule()) + "("
323: + factoryCall + "());";
324: return codeLines;
325: }
326:
327: protected String[] getTearDownMethodCodeLines(MemberInfo memberInfo) {
328: return new String[] { "super.tearDown();",
329: memberInfo.getModuleMember() + " = null;" };
330: }
331:
332: protected void addAdditionalControlMethods(
333: JavaClassGenerator classGenerator, MemberInfo memberInfo) {
334: addHTMLOutputMethods(classGenerator, memberInfo);
335: addGetAndSetMethodPair(classGenerator, memberInfo.getModule(),
336: memberInfo.getModuleMember());
337: }
338:
339: protected String[] getClassComment(Class module) {
340: String link = getJavaDocLink(module);
341: String[] comment = new String[7];
342: comment[0] = "Delegator for " + link + ". You can";
343: comment[1] = "subclass this adapter or use " + link;
344: comment[2] = "directly (so your test case can use another base class).";
345: comment[3] = "This adapter extends "
346: + getJavaDocLink(BaseTestCase.class) + ".";
347: comment[4] = "It can be used if you want to use several modules in conjunction.";
348: comment[5] = "<b>This class is generated from the " + link;
349: comment[6] = "and should not be edited directly</b>.";
350: return comment;
351: }
352:
353: protected Class getSuperClass(Class module) {
354: if (HTMLOutputModule.class.isAssignableFrom(module)) {
355: return HTMLOutputTestCase.class;
356: }
357: return BaseTestCase.class;
358: }
359:
360: protected String getJavaFileName(Class module, String className) {
361: return ClassUtil.getPackageName(module).replace('.', '/') + "/"
362: + className + ".java";
363: }
364:
365: protected String getClassNameFromBaseName(String className) {
366: return className + "CaseAdapter";
367: }
368:
369: protected class MemberInfo {
370: private Class module;
371: private String moduleMember;
372: private Class factory;
373: private String factoryMember;
374:
375: public Class getFactory() {
376: return factory;
377: }
378:
379: public void setFactory(Class factory) {
380: this .factory = factory;
381: }
382:
383: public String getFactoryMember() {
384: return factoryMember;
385: }
386:
387: public void setFactoryMember(String factoryMember) {
388: this .factoryMember = factoryMember;
389: }
390:
391: public Class getModule() {
392: return module;
393: }
394:
395: public void setModule(Class module) {
396: this .module = module;
397: }
398:
399: public String getModuleMember() {
400: return moduleMember;
401: }
402:
403: public void setModuleMember(String moduleMember) {
404: this.moduleMember = moduleMember;
405: }
406: }
407: }
|