001: package com.mockrunner.gen.proc;
002:
003: import java.lang.reflect.Modifier;
004: import java.util.ArrayList;
005: import java.util.List;
006: import java.util.Set;
007:
008: import com.mockrunner.util.common.ArrayUtil;
009: import com.mockrunner.util.common.ClassUtil;
010: import com.mockrunner.util.common.StringUtil;
011:
012: public class JavaClassGenerator {
013: private Package packageInfo;
014: private List imports;
015: private String className;
016: private boolean isAbstract;
017: private Class super Class;
018: private List interfaces;
019: private List memberTypes;
020: private List memberNames;
021: private String[] classCommentLines;
022: private boolean createJavaDocComments;
023: private List methods;
024: private List constructors;
025:
026: public JavaClassGenerator() {
027: reset();
028: }
029:
030: public void reset() {
031: imports = new ArrayList();
032: interfaces = new ArrayList();
033: memberTypes = new ArrayList();
034: memberNames = new ArrayList();
035: createJavaDocComments = true;
036: isAbstract = false;
037: methods = new ArrayList();
038: constructors = new ArrayList();
039: }
040:
041: public void setCreateJavaDocComments(boolean createJavaDocComments) {
042: this .createJavaDocComments = createJavaDocComments;
043: }
044:
045: public void setPackage(Package packageInfo) {
046: this .packageInfo = packageInfo;
047: }
048:
049: public void setClassName(String className) {
050: this .className = className;
051: }
052:
053: public void setAbstract(boolean isAbstract) {
054: this .isAbstract = isAbstract;
055: }
056:
057: public void setSuperClass(Class super Class) {
058: this .super Class = super Class;
059: }
060:
061: public void addImport(Class importClass) {
062: imports.add(importClass.getName());
063: }
064:
065: public void addInterfaceImplementation(Class interfaceClass) {
066: interfaces.add(interfaceClass);
067: }
068:
069: public void setClassComment(String[] commentLines) {
070: classCommentLines = (String[]) ArrayUtil
071: .copyArray(commentLines);
072: }
073:
074: public void addMemberDeclaration(Class memberType, String name) {
075: memberTypes.add(memberType);
076: memberNames.add(name);
077: }
078:
079: public void addConstructorDeclaration() {
080: constructors.add(new ConstructorDeclaration());
081: }
082:
083: public void addConstructorDeclaration(
084: ConstructorDeclaration constructor) {
085: constructors.add(constructor);
086: }
087:
088: public void addMethodDeclaration(MethodDeclaration method) {
089: methods.add(method);
090: }
091:
092: public String generate() {
093: JavaLineAssembler assembler = new JavaLineAssembler();
094: assembler.appendPackageInfo(getPackageName());
095: appendImportBlocks(assembler);
096: appendCommentBlock(assembler, classCommentLines);
097: if (isAbstract) {
098: assembler
099: .appendClassDefintion(className, Modifier
100: .toString(Modifier.ABSTRACT),
101: getClassName(super Class),
102: getClassNames(interfaces));
103: } else {
104: assembler
105: .appendClassDefintion(className, "",
106: getClassName(super Class),
107: getClassNames(interfaces));
108: }
109: assembler.appendLeftBrace();
110: assembler.appendNewLine();
111: assembler.setIndentLevel(1);
112: appendMembers(assembler);
113: appendConstructors(assembler);
114: appendMethods(assembler);
115: assembler.setIndentLevel(0);
116: assembler.appendRightBrace();
117: return assembler.getResult();
118: }
119:
120: private String getPackageName() {
121: if (null != packageInfo) {
122: return packageInfo.getName();
123: }
124: return null;
125: }
126:
127: private String getClassName(Class clazz) {
128: if (null == clazz)
129: return null;
130: return ClassUtil.getClassName(clazz);
131: }
132:
133: private String[] getClassNames(List classList) {
134: if (null == classList || classList.size() <= 0)
135: return null;
136: List nameList = new ArrayList();
137: for (int ii = 0; ii < classList.size(); ii++) {
138: Class clazz = (Class) classList.get(ii);
139: if (null != clazz) {
140: nameList.add(getClassName(clazz));
141: }
142: }
143: return (String[]) nameList.toArray(new String[nameList.size()]);
144: }
145:
146: private String[] getClassNames(Class[] arguments) {
147: if (null == arguments || arguments.length <= 0)
148: return null;
149: String[] names = new String[arguments.length];
150: for (int ii = 0; ii < arguments.length; ii++) {
151: Class clazz = arguments[ii];
152: names[ii] = getClassName(clazz);
153: }
154: return names;
155: }
156:
157: private String[] getArgumentNames(Class[] arguments,
158: String[] argumentNames) {
159: if (null == arguments || arguments.length <= 0)
160: return null;
161: if (null != argumentNames
162: && argumentNames.length >= arguments.length)
163: return argumentNames;
164: if (null == argumentNames)
165: argumentNames = new String[0];
166: String[] newNames = new String[arguments.length];
167: for (int ii = 0; ii < argumentNames.length; ii++) {
168: newNames[ii] = argumentNames[ii];
169: }
170: for (int ii = argumentNames.length; ii < arguments.length; ii++) {
171: newNames[ii] = ClassUtil.getArgumentName(arguments[ii]);
172: }
173: ArrayUtil.ensureUnique(newNames);
174: return newNames;
175: }
176:
177: private void appendImportBlocks(JavaLineAssembler assembler) {
178: List importBlocks = processImports();
179: for (int ii = 0; ii < importBlocks.size(); ii++) {
180: Set currentBlock = (Set) importBlocks.get(ii);
181: assembler.appendImports(new ArrayList(currentBlock));
182: assembler.appendNewLine();
183: }
184: }
185:
186: private void appendCommentBlock(JavaLineAssembler assembler,
187: String[] commentLines) {
188: if (createJavaDocComments) {
189: assembler.appendJavaDocComment(commentLines);
190: } else {
191: assembler.appendBlockComment(commentLines);
192: }
193: }
194:
195: private void appendMembers(JavaLineAssembler assembler) {
196: String[] memberTypeNames = getClassNames(memberTypes);
197: if (null == memberTypeNames)
198: return;
199: for (int ii = 0; ii < memberTypeNames.length; ii++) {
200: assembler.appendMemberDeclaration(memberTypeNames[ii],
201: (String) memberNames.get(ii));
202: }
203: }
204:
205: private void appendMethods(JavaLineAssembler assembler) {
206: for (int ii = 0; ii < methods.size(); ii++) {
207: MethodDeclaration declaration = (MethodDeclaration) methods
208: .get(ii);
209: appendMethodHeader(assembler, declaration);
210: String[] modifiers = prepareModifiers(declaration
211: .getModifier());
212: String returnType = getClassName(declaration
213: .getReturnType());
214: String[] argumentTypes = getClassNames(declaration
215: .getArguments());
216: String[] exceptionTypes = getClassNames(declaration
217: .getExceptions());
218: String[] argumentNames = getArgumentNames(declaration
219: .getArguments(), declaration.getArgumentNames());
220: assembler.appendMethodDeclaration(modifiers, returnType,
221: declaration.getName(), argumentTypes,
222: argumentNames, exceptionTypes);
223: appendMethodBody(assembler, declaration);
224: }
225: }
226:
227: private void appendConstructors(JavaLineAssembler assembler) {
228: for (int ii = 0; ii < constructors.size(); ii++) {
229: ConstructorDeclaration declaration = (ConstructorDeclaration) constructors
230: .get(ii);
231: appendMethodHeader(assembler, declaration);
232: String[] argumentTypes = getClassNames(declaration
233: .getArguments());
234: String[] exceptionTypes = getClassNames(declaration
235: .getExceptions());
236: String[] argumentNames = getArgumentNames(declaration
237: .getArguments(), declaration.getArgumentNames());
238: assembler.appendConstructorDeclaration(className,
239: argumentTypes, argumentNames, exceptionTypes);
240: appendMethodBody(assembler, declaration);
241: }
242: }
243:
244: private void appendMethodHeader(JavaLineAssembler assembler,
245: ConstructorDeclaration declaration) {
246: assembler.appendNewLine();
247: appendCommentBlock(assembler, declaration.getCommentLines());
248: }
249:
250: private void appendMethodBody(JavaLineAssembler assembler,
251: ConstructorDeclaration declaration) {
252: assembler.appendIndent();
253: assembler.appendLeftBrace();
254: assembler.appendNewLine();
255: appendCodeLines(assembler, declaration.getCodeLines());
256: assembler.appendIndent();
257: assembler.appendRightBrace();
258: assembler.appendNewLine();
259: }
260:
261: private void appendCodeLines(JavaLineAssembler assembler,
262: String[] codeLines) {
263: assembler.setIndentLevel(2);
264: assembler.appendCodeLines(codeLines);
265: assembler.setIndentLevel(1);
266: }
267:
268: private String[] prepareModifiers(int modifier) {
269: String modifierString = Modifier.toString(modifier);
270: if (null == modifierString
271: || modifierString.trim().length() <= 0)
272: return null;
273: return StringUtil.split(modifierString, " ", true);
274: }
275:
276: private List processImports() {
277: addMissingImports();
278: PackageImportSorter sorter = new PackageImportSorter();
279: return sorter.sortBlocks(imports);
280: }
281:
282: private void addMissingImports() {
283: addImportIfNecessary(super Class);
284: addImportsIfNecessary(interfaces);
285: addImportsIfNecessary(memberTypes);
286: for (int ii = 0; ii < constructors.size(); ii++) {
287: ConstructorDeclaration declaration = (ConstructorDeclaration) constructors
288: .get(ii);
289: addImportsForArguments(declaration);
290: addImportsForExceptions(declaration);
291: }
292: for (int ii = 0; ii < methods.size(); ii++) {
293: MethodDeclaration declaration = (MethodDeclaration) methods
294: .get(ii);
295: addImportForReturnType(declaration);
296: addImportsForArguments(declaration);
297: addImportsForExceptions(declaration);
298: }
299: }
300:
301: private void addImportsForExceptions(
302: ConstructorDeclaration declaration) {
303: Class[] exceptions = declaration.getExceptions();
304: if (null == exceptions || exceptions.length <= 0)
305: return;
306: for (int ii = 0; ii < exceptions.length; ii++) {
307: addImportIfNecessary(exceptions[ii]);
308: }
309: }
310:
311: private void addImportsForArguments(
312: ConstructorDeclaration declaration) {
313: Class[] arguments = declaration.getArguments();
314: if (null == arguments || arguments.length <= 0)
315: return;
316: for (int ii = 0; ii < arguments.length; ii++) {
317: addImportIfNecessary(arguments[ii]);
318: }
319: }
320:
321: private void addImportForReturnType(MethodDeclaration declaration) {
322: Class returnType = declaration.getReturnType();
323: if (null == returnType)
324: return;
325: addImportIfNecessary(returnType);
326: }
327:
328: private void addImportsIfNecessary(List classes) {
329: if (null == classes)
330: return;
331: for (int ii = 0; ii < classes.size(); ii++) {
332: addImportIfNecessary((Class) classes.get(ii));
333: }
334: }
335:
336: private void addImportIfNecessary(Class clazz) {
337: if (null == clazz)
338: return;
339: while (clazz.isArray())
340: clazz = clazz.getComponentType();
341: if (imports.contains(clazz.getName()))
342: return;
343: if (clazz.getName().startsWith("java.lang"))
344: return;
345: if (belongsToSamePackage(clazz))
346: return;
347: if (clazz.isPrimitive())
348: return;
349: addImport(clazz);
350: }
351:
352: private boolean belongsToSamePackage(Class clazz) {
353: String this PackageName = getPackageName();
354: Package classPackage = clazz.getPackage();
355: String classPackageName = "";
356: if (null != classPackage) {
357: classPackageName = classPackage.getName();
358: }
359: if (null == this PackageName) {
360: this PackageName = "";
361: }
362: if (null == classPackageName) {
363: classPackageName = "";
364: }
365: return this PackageName.equals(classPackageName);
366: }
367:
368: public static class ConstructorDeclaration {
369: private Class[] arguments;
370: private String[] argumentNames;
371: private String[] codeLines;
372: private String[] commentLines;
373: private Class[] exceptions;
374:
375: public String[] getCodeLines() {
376: if (null == codeLines)
377: return null;
378: return (String[]) ArrayUtil.copyArray(codeLines);
379: }
380:
381: public void setCodeLines(String[] codeLines) {
382: this .codeLines = (String[]) ArrayUtil.copyArray(codeLines);
383: }
384:
385: public String[] getCommentLines() {
386: if (null == commentLines)
387: return null;
388: return (String[]) ArrayUtil.copyArray(commentLines);
389: }
390:
391: public void setCommentLines(String[] commentLines) {
392: this .commentLines = (String[]) ArrayUtil
393: .copyArray(commentLines);
394: }
395:
396: public String[] getArgumentNames() {
397: if (null == argumentNames)
398: return null;
399: return (String[]) ArrayUtil.copyArray(argumentNames);
400: }
401:
402: public void setArgumentNames(String[] argumentNames) {
403: this .argumentNames = (String[]) ArrayUtil
404: .copyArray(argumentNames);
405: }
406:
407: public Class[] getArguments() {
408: if (null == arguments)
409: return null;
410: return (Class[]) ArrayUtil.copyArray(arguments);
411: }
412:
413: public void setArguments(Class[] arguments) {
414: this .arguments = (Class[]) ArrayUtil.copyArray(arguments);
415: }
416:
417: public Class[] getExceptions() {
418: if (null == exceptions)
419: return null;
420: return (Class[]) ArrayUtil.copyArray(exceptions);
421: }
422:
423: public void setExceptions(Class[] exceptions) {
424: this .exceptions = (Class[]) ArrayUtil.copyArray(exceptions);
425: }
426: }
427:
428: public static class MethodDeclaration extends
429: ConstructorDeclaration {
430: private int modifier;
431: private Class returnType;
432: private String name;
433:
434: public MethodDeclaration() {
435: this ("method");
436: }
437:
438: public MethodDeclaration(String name) {
439: this (Modifier.PUBLIC, name);
440: }
441:
442: public MethodDeclaration(int modifier, String name) {
443: this (modifier, name, Void.TYPE);
444: }
445:
446: public MethodDeclaration(int modifier, String name,
447: Class returnType) {
448: setModifier(modifier);
449: setReturnType(returnType);
450: setName(name);
451: }
452:
453: public int getModifier() {
454: return modifier;
455: }
456:
457: public void setModifier(int modifier) {
458: this .modifier = modifier;
459: }
460:
461: public String getName() {
462: return name;
463: }
464:
465: public void setName(String name) {
466: this .name = name;
467: }
468:
469: public Class getReturnType() {
470: return returnType;
471: }
472:
473: public void setReturnType(Class returnType) {
474: this.returnType = returnType;
475: }
476: }
477: }
|