001: package net.sf.mockcreator.codegeneration;
002:
003: import java.lang.reflect.Constructor;
004: import java.lang.reflect.Field;
005: import java.lang.reflect.Method;
006: import java.util.ArrayList;
007: import java.util.HashMap;
008: import java.util.HashSet;
009: import java.util.Iterator;
010: import java.util.List;
011: import java.util.Map;
012: import java.util.Set;
013:
014: import net.sf.mockcreator.exceptions.MockException;
015: import net.sf.mockcreator.utils.SourceUtils;
016:
017: public class MockDescriptor {
018:
019: private String name;
020: private List /* of java.lang.reflect.Constructor */ctors = new ArrayList();
021: private boolean isInterface;
022: private List /* of java.lang.reflect.Method */methods = new ArrayList();
023: private String sourceFQN;
024: private String sourcePackageName;
025: private String targetPackageName;
026: private String outputPath;
027: public int fakeDepth;
028: private Class clazz;
029: private Map usedNames = new HashMap();
030: private boolean inner = false;
031:
032: public MockDescriptor(String typeName) {
033: init(typeName, null);
034: }
035:
036: public MockDescriptor(String typeName, String[] classpathDirs) {
037: init(typeName, classpathDirs);
038: }
039:
040: public List getCtors() {
041: return ctors;
042: }
043:
044: public List getMethods() {
045: return methods;
046: }
047:
048: public String getMockName() {
049: int dotPos = getName().indexOf(".");
050:
051: if (dotPos < 0) {
052: return "Mock" + getName();
053: }
054: return "Mock" + getName().substring(dotPos + 1);
055: }
056:
057: public String getSourceFQN() {
058: return sourceFQN;
059: }
060:
061: public String getSourcePackageName() {
062: return sourcePackageName;
063: }
064:
065: public String getTargetFullName() {
066: if (isTargetInDefaultPackage()) {
067: return "Mock" + getName();
068: }
069: return getTargetPackageName() + ".Mock" + getName();
070: }
071:
072: public String getTargetPackageName() {
073: return targetPackageName;
074: }
075:
076: public static int hashCode(String name, int modifiers, Class ret,
077: Class[] params, Class[] exceptions) {
078: int retCode = 0;
079: int hashCode = name.hashCode();
080:
081: hashCode += modifiers;
082: retCode += hashCode;
083:
084: hashCode += ((ret == null) ? 0 : ret.getName().hashCode());
085: retCode += hashCode;
086:
087: for (int i = 0; i < params.length; ++i) {
088: hashCode += params[i].getName().hashCode();
089: retCode += hashCode;
090: }
091:
092: for (int i = 0; i < exceptions.length; ++i) {
093: hashCode += exceptions[i].getName().hashCode();
094: retCode += hashCode;
095: }
096:
097: return retCode;
098: }
099:
100: /**
101: * Compares method with no regards to it's declaring class.
102: */
103: private static boolean methodsEqual(Method m1, Method m2) {
104: if (!m1.getName().equals(m2.getName()))
105: return false;
106:
107: Class[] ps1 = m1.getParameterTypes();
108: Class[] ps2 = m2.getParameterTypes();
109:
110: if (ps1.length != ps2.length)
111: return false;
112: for (int j = 0; j < ps1.length; j++) {
113: if (!ps1[j].getName().equals(ps2[j].getName()))
114: return false;
115: }
116:
117: return true;
118: }
119:
120: /**
121: * Checks is the method was originally defined in java.lang.Object
122: */
123: private static boolean theObjectClassContainsMethod(Method method) {
124: Method[] oms = Object.class.getMethods();
125: for (int i = 0; i < oms.length; i++) {
126: // TODO: replace with Method.equals()
127: if (methodsEqual(method, oms[i]))
128: return true;
129: }
130:
131: return false;
132: }
133:
134: /**
135: * The method generates hashcode that uniquelly matches interface methods
136: * and constructors. If the hash mismatches for an interface we may for sure
137: * say that it has changed.
138: */
139: public int hashCode() {
140: int hashCode = 0;
141:
142: Iterator it = getMethods().iterator();
143:
144: while (it.hasNext()) {
145: Method m = (Method) it.next();
146: hashCode += hashCode(m.getName(), m.getModifiers(), m
147: .getReturnType(), m.getParameterTypes(), m
148: .getExceptionTypes());
149: }
150:
151: it = getCtors().iterator();
152:
153: while (it.hasNext()) {
154: Constructor m = (Constructor) it.next();
155: hashCode += hashCode(m.getName(), m.getModifiers(), null, m
156: .getParameterTypes(), m.getExceptionTypes());
157: }
158:
159: return hashCode;
160: }
161:
162: public boolean isInterface() {
163: return isInterface;
164: }
165:
166: public boolean isSourceInDefaultPackage() {
167: return (getSourcePackageName() == null)
168: || (getSourcePackageName().trim().length() == 0);
169: }
170:
171: public boolean isTargetInDefaultPackage() {
172: return (getTargetPackageName() == null)
173: || (getTargetPackageName().trim().length() == 0);
174: }
175:
176: public void setCtors(List ctors) {
177: this .ctors = ctors;
178: }
179:
180: public void setInterface(boolean isInterface) {
181: this .isInterface = isInterface;
182: }
183:
184: public void setMethods(List methods) {
185: this .methods = methods;
186: }
187:
188: public void setSourceFQN(String sourceFQN) {
189: this .sourceFQN = sourceFQN;
190: }
191:
192: public void setSourcePackageName(String sourcePackageName) {
193: this .sourcePackageName = sourcePackageName;
194: }
195:
196: public void setTargetPackageName(String targetPackageName) {
197: this .targetPackageName = targetPackageName;
198: }
199:
200: public void setName(String name) {
201: this .name = name;
202: }
203:
204: public String getName() {
205: return name;
206: }
207:
208: public int getFakeDepth() {
209: return fakeDepth;
210: }
211:
212: public void setFakeDepth(int fakeDepth) {
213: this .fakeDepth = fakeDepth;
214: }
215:
216: private void init(String typeName, String[] classpathDirs)
217: throws MockException {
218: ClassLoader cl = Thread.currentThread().getContextClassLoader();
219:
220: try {
221: ClassLoader my = new DirClassLoader(cl, classpathDirs);
222: Thread.currentThread().setContextClassLoader(my);
223:
224: clazz = my.loadClass(typeName);
225: try {
226: Field f = clazz.getField(MockBuilder.BASENAME);
227: typeName = (String) f.get(null);
228: // ok, update
229: clazz = my.loadClass(typeName);
230: } catch (Exception ex) {
231: }
232:
233: int lastDot = typeName.lastIndexOf('.');
234: name = lastDot > 0 ? typeName.substring(lastDot + 1)
235: : typeName;
236: int lastDollar = name.lastIndexOf('$');
237: if (lastDollar > 0) {
238: inner = true;
239: name = name.substring(lastDollar + 1);
240: }
241:
242: setSourcePackageName(typeName.substring(0,
243: (lastDot > 0) ? lastDot : 0));
244:
245: // avoid generating stuff into java.* package (loading prohibited by classloader)
246: targetPackageName = getSourcePackageName();
247: if (targetPackageName.startsWith("java."))
248: targetPackageName = "mockcreator." + targetPackageName;
249:
250: isInterface = clazz.isInterface();
251: loadCtors(clazz);
252: loadMethods(clazz);
253:
254: setSourceFQN(typeName);
255: } catch (Throwable th) {
256: throw new MockException("failed to init a MockDescriptor",
257: th);
258: } finally {
259: Thread.currentThread().setContextClassLoader(cl);
260: }
261: }
262:
263: private void loadCtors(Class cls) {
264: java.lang.reflect.Constructor[] ctrs = cls
265: .getDeclaredConstructors();
266: setCtors(new ArrayList());
267:
268: for (int i = 0; i < ctrs.length; ++i) {
269: getCtors().add(ctrs[i]);
270: }
271: }
272:
273: private void loadMethod(Set unique, Method mth) {
274: // dozen: we do not want to overload toString, hashCode and equals,
275: // even if they was redefined in mocked class
276: if (theObjectClassContainsMethod(mth)) {
277: return;
278: }
279:
280: if (SourceUtils.isBridgeOrSynthetic(mth)) {
281: return;
282: }
283:
284: String uniqueName = SourceUtils.getMethodFQN(mth.getName(), mth
285: .getParameterTypes(), null);
286: if (unique.contains(uniqueName)) {
287: return;
288: }
289:
290: unique.add(uniqueName);
291: methods.add(mth);
292: }
293:
294: private void loadMethods(Class cls) {
295: // dozen: interface A has method foo; interface B extends A and
296: // has method foo too. Then, cls.getMethods() returns duplicates.
297: // Thanks to Tobias Friedrich for the bug report.
298: loadMethods(new HashSet(), cls);
299: }
300:
301: private void loadMethods(Set unique, Class cls) {
302: if (cls == null)
303: return;
304:
305: java.lang.reflect.Method[] mths = cls.getDeclaredMethods();
306: for (int i = 0; i < mths.length; ++i) {
307: loadMethod(unique, mths[i]);
308: }
309:
310: mths = cls.getMethods();
311: for (int i = 0; i < mths.length; ++i) {
312: loadMethod(unique, mths[i]);
313: }
314:
315: loadMethods(unique, cls.getSuperclass());
316: }
317:
318: public String getSuperClassName() {
319: Class cls = clazz.getSuperclass();
320: if (cls == null)
321: return null;
322: return cls.getName().replace('$', '.');
323: }
324:
325: public List getInterfaceNames() {
326: Class[] ifaces = clazz.getInterfaces();
327: List lst = new ArrayList();
328: if (ifaces != null) {
329: for (int i = 0; i < ifaces.length; i++) {
330: Class cls = ifaces[i];
331: lst.add(cls.getName());
332: }
333: }
334: return lst;
335: }
336:
337: public String getOutputPath() {
338: return outputPath;
339: }
340:
341: public void setOutputPath(String outputPath) {
342: this .outputPath = outputPath;
343: }
344:
345: public Class getSourceClass() {
346: return clazz;
347: }
348:
349: public Map getSignatures() {
350: return usedNames;
351: }
352:
353: public void setInner(boolean inner) {
354: this .inner = inner;
355: }
356:
357: public boolean isInner() {
358: return inner;
359: }
360: }
|