1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package net.sf.jour.signature;
25
26 import java.io.ByteArrayInputStream;
27 import java.io.IOException;
28 import java.net.URL;
29 import java.util.List;
30 import java.util.StringTokenizer;
31 import java.util.Vector;
32
33 import javassist.CannotCompileException;
34 import javassist.ClassPool;
35 import javassist.CtBehavior;
36 import javassist.CtClass;
37 import javassist.CtConstructor;
38 import javassist.CtField;
39 import javassist.CtMethod;
40 import javassist.Modifier;
41 import javassist.NotFoundException;
42
43 import javax.xml.parsers.ParserConfigurationException;
44
45 import net.sf.jour.ConfigException;
46 import net.sf.jour.instrumentor.MakeEmptyMethodInstrumentor;
47 import net.sf.jour.log.Logger;
48 import net.sf.jour.util.ConfigFileUtil;
49 import net.sf.jour.util.FileUtil;
50
51 import org.w3c.dom.Document;
52 import org.w3c.dom.Node;
53 import org.w3c.dom.NodeList;
54 import org.xml.sax.SAXException;
55
56
57
58
59
60 public class SignatureImport {
61
62 protected static final Logger log = Logger.getLogger();
63
64 static final boolean createableObject = false;
65
66
67 static final boolean editableObject = true;
68
69 static final boolean editableObjectConstructor = false;
70
71 static final String OBJECT_CLASS_NAME = "java.lang.Object";
72
73 private ClassPool classPool;
74
75 private List classNames = new Vector();
76
77 private List classes = new Vector();
78
79 private APIFilter filter;
80
81 private String stubException;
82
83 private String stubExceptionMessage;
84
85 public SignatureImport(boolean useSystemClassPath, String supportingJars) {
86 classPool = new ClassPool();
87 if (supportingJars != null) {
88 try {
89 classPool.appendPathList(supportingJars);
90 } catch (NotFoundException e) {
91 throw new RuntimeException(e);
92 }
93 }
94 if (useSystemClassPath) {
95 classPool.appendSystemPath();
96 }
97 }
98
99 public ClassPool getClassPool() {
100 return classPool;
101 }
102
103 public List getClasses() {
104 return this.classes;
105 }
106
107 public List getClassNames() {
108 return this.classNames;
109 }
110
111 public void setStubException(String property) {
112 this.stubException = property;
113 }
114
115 public void setStubExceptionMessage(String property) {
116 this.stubExceptionMessage = property;
117 if ((this.stubException == null) && (property != null)) {
118 this.stubException = "java.lang.RuntimeException";
119 }
120 }
121
122 public void load(String xmlFileName) {
123 load(xmlFileName, null);
124 }
125
126 public void load(String xmlFileName, APIFilter filter) {
127 if (xmlFileName == null) {
128 throw new ConfigException("Signature File required");
129 }
130 URL location = FileUtil.getFile(xmlFileName);
131 if (location == null) {
132 throw new ConfigException("File Not found " + xmlFileName);
133 }
134 if (filter == null) {
135 this.filter = APIFilter.ALL;
136 } else {
137 this.filter = filter;
138 }
139 try {
140 Document xmlDoc = ConfigFileUtil.loadDocument(location);
141 Node rootNode = ConfigFileUtil.getFirstElement(xmlDoc, ExportXML.rootNodeName);
142 if (rootNode == null) {
143 throw new ConfigException("Invalid XML root");
144 }
145
146 NodeList classNodeList = rootNode.getChildNodes();
147 for (int j = 0; j < classNodeList.getLength(); j++) {
148 Node node = classNodeList.item(j);
149 if ("interface".equals(node.getNodeName())) {
150 CtClass c = loadInterface(node);
151 if (this.filter.isAPIClass(c)) {
152 this.classes.add(c);
153 classNames.add(c.getName());
154 }
155 } else if ("class".equals(node.getNodeName())) {
156 CtClass i = loadClass(node);
157 if (this.filter.isAPIClass(i)) {
158 this.classes.add(i);
159 classNames.add(i.getName());
160 }
161 } else if (node.hasChildNodes()) {
162 throw new ConfigException("Invalid XML node " + node.getNodeName());
163 }
164 }
165 for (int j = 0; j < classNodeList.getLength(); j++) {
166 Node node = classNodeList.item(j);
167 if ("class".equals(node.getNodeName())) {
168 updateClass(node);
169 }
170 }
171 } catch (ParserConfigurationException e) {
172 throw new ConfigException("Error parsing XML", e);
173 } catch (SAXException e) {
174 throw new ConfigException("Error parsing XML", e);
175 } catch (IOException e) {
176 throw new ConfigException("Error parsing XML", e);
177 }
178 }
179
180 private CtClass loadInterface(Node node) {
181 CtClass klass = createInterface(node);
182 int mod = decodeModifiers(ConfigFileUtil.getNodeAttribute(node, "modifiers"));
183 mod |= Modifier.INTERFACE | Modifier.ABSTRACT;
184 klass.setModifiers(mod);
185
186 loadHierarchy(klass, node);
187 loadMethods(klass, node);
188 loadFields(klass, node);
189 return klass;
190 }
191
192 private CtClass loadClass(Node node) {
193 CtClass klass = createClass(node);
194
195 int mod = decodeModifiers(ConfigFileUtil.getNodeAttribute(node, "modifiers"));
196 klass.setModifiers(mod);
197
198 loadHierarchy(klass, node);
199 loadConstructors(klass, node);
200 loadMethods(klass, node);
201 loadFields(klass, node);
202 return klass;
203 }
204
205 private void updateClass(Node node) {
206 String classname = ConfigFileUtil.getNodeAttribute(node, "name");
207 if (!editableObject && OBJECT_CLASS_NAME.equals(classname)) {
208 return;
209 }
210 CtClass klass;
211 try {
212 klass = classPool.get(classname);
213 } catch (NotFoundException e) {
214 throw new RuntimeException(classname + " class is missing");
215 }
216 if (!this.filter.isAPIClass(klass)) {
217 return;
218 }
219 if (!editableObjectConstructor && OBJECT_CLASS_NAME.equals(classname)) {
220
221 } else {
222 updateConstructors(klass, node);
223 }
224 updateMethods(klass, node);
225 }
226
227 private CtClass createClass(Node node) {
228 String classname = ConfigFileUtil.getNodeAttribute(node, "name");
229 String superclassName = ConfigFileUtil.getNodeAttribute(node, "extends");
230
231 if (!createableObject && OBJECT_CLASS_NAME.equals(classname)) {
232 return createEmptyObjectClass();
233 }
234
235 try {
236 CtClass exists = classPool.get(classname);
237 exists.detach();
238 } catch (NotFoundException e) {
239
240 }
241
242 return classPool.makeClass(classname, createClass(superclassName));
243 }
244
245 private CtClass createClass(String classname) {
246 if (classname == null) {
247 return null;
248 }
249 CtClass klass;
250 try {
251 klass = classPool.get(classname);
252 } catch (NotFoundException e) {
253 klass = classPool.makeClass(classname);
254 }
255 return klass;
256 }
257
258 private CtClass createEmptyObjectClass() {
259 ClassPool defaultPool = ClassPool.getDefault();
260 CtClass klass;
261 try {
262 klass = defaultPool.get(OBJECT_CLASS_NAME);
263
264 klass.detach();
265 CtConstructor init = klass.getClassInitializer();
266 if (init != null) {
267 klass.removeConstructor(init);
268 }
269 CtMethod[] methods = klass.getDeclaredMethods();
270 for (int i = 0; i < methods.length; i++) {
271 klass.removeMethod(methods[i]);
272 }
273 CtField[] fields = klass.getFields();
274 for (int i = 0; i < fields.length; i++) {
275 klass.removeField(fields[i]);
276 }
277 } catch (NotFoundException e) {
278 throw new RuntimeException("Can't create class java.lang.Object", e);
279 }
280 try {
281 classPool.makeClass(new ByteArrayInputStream(klass.toBytecode()));
282 return classPool.get(OBJECT_CLASS_NAME);
283 } catch (IOException e) {
284 throw new RuntimeException("Can't create class java.lang.Object", e);
285 } catch (NotFoundException e) {
286 throw new RuntimeException("Can't create class java.lang.Object", e);
287 } catch (CannotCompileException e) {
288 throw new RuntimeException("Can't create class java.lang.Object", e);
289 }
290 }
291
292 private CtClass createInterface(Node node) {
293 String classname = ConfigFileUtil.getNodeAttribute(node, "name");
294 String superclassName = ConfigFileUtil.getNodeAttribute(node, "extends");
295
296 try {
297 CtClass exists = classPool.get(classname);
298 exists.detach();
299 } catch (NotFoundException e) {
300 }
301
302 return classPool.makeInterface(classname, createInterface(superclassName));
303 }
304
305 private CtClass createInterface(String classname) {
306 if (classname == null) {
307 return null;
308 }
309 CtClass klass;
310 try {
311 klass = classPool.get(classname);
312 } catch (NotFoundException e) {
313 klass = classPool.makeInterface(classname);
314 }
315 return klass;
316 }
317
318 private void loadHierarchy(CtClass klass, Node node) {
319 Node implementNode = ConfigFileUtil.getChildNode(node, "implements");
320 if (implementNode == null) {
321 return;
322 }
323 Node[] interfaceList = ConfigFileUtil.getChildNodes(implementNode, "interface");
324 for (int i = 0; i < interfaceList.length; i++) {
325 Node interfaceNode = interfaceList[i];
326 String interfaceName = ConfigFileUtil.getNodeAttribute(interfaceNode, "name");
327 klass.addInterface(createInterface(interfaceName));
328 }
329 }
330
331 private CtClass[] getParameters(Node node) {
332 Node[] partNodes = ConfigFileUtil.getChildNodes(node, "parameter");
333 CtClass[] parameters = new CtClass[partNodes.length];
334 for (int j = 0; j < partNodes.length; j++) {
335 parameters[j] = createInterface(ConfigFileUtil.getNodeAttribute(partNodes[j], "type"));
336 if (parameters[j] == null) {
337 throw new RuntimeException("parameter " + j + " type is missing");
338 }
339 }
340 return parameters;
341 }
342
343 private void loadConstructors(CtClass klass, Node node) {
344 Node[] list = ConfigFileUtil.getChildNodes(node, "constructor");
345 boolean defaultConstructorLoaded = false;
346 for (int i = 0; i < list.length; i++) {
347 int modifiers = getModifiers(list[i]);
348 CtClass[] parameters = getParameters(list[i]);
349 if (parameters.length != 0) {
350 if (!this.filter.isAPIModifier(modifiers)) {
351 continue;
352 }
353 }
354
355 CtConstructor c;
356 try {
357 c = klass.getDeclaredConstructor(parameters);
358 } catch (NotFoundException e) {
359 c = new CtConstructor(parameters, klass);
360
361 try {
362 klass.addConstructor(c);
363 } catch (CannotCompileException e2) {
364 throw new RuntimeException(klass.getName(), e2);
365 }
366 }
367 loadExceptions(c, list[i]);
368 c.setModifiers(modifiers);
369 if (parameters.length == 0) {
370 defaultConstructorLoaded = true;
371 }
372 }
373 if (!defaultConstructorLoaded) {
374 CtConstructor defaultConstructor;
375 try {
376 defaultConstructor = klass.getDeclaredConstructor(new CtClass[0]);
377 } catch (NotFoundException e) {
378 defaultConstructor = new CtConstructor(new CtClass[0], klass);
379
380 try {
381 klass.addConstructor(defaultConstructor);
382 } catch (CannotCompileException e2) {
383 throw new RuntimeException(klass.getName(), e2);
384 }
385 }
386 defaultConstructor.setModifiers(Modifier.PRIVATE);
387 }
388 }
389
390 private void updateConstructors(CtClass klass, Node node) {
391 CtConstructor[] constructors = klass.getDeclaredConstructors();
392 for (int i = 0; i < constructors.length; i++) {
393 try {
394 constructors[i].setBody(emptyBodyCode(CtClass.voidType));
395 } catch (CannotCompileException ce) {
396 throw new RuntimeException(klass.getName(), ce);
397 }
398 }
399 }
400
401 private void updateMethods(CtClass klass, Node node) {
402 if (klass.isInterface()) {
403 return;
404 }
405 CtMethod[] methods = klass.getDeclaredMethods();
406 for (int i = 0; i < methods.length; i++) {
407 CtMethod method = methods[i];
408 if (!Modifier.isAbstract(method.getModifiers())) {
409 try {
410 method.setBody(emptyBodyCode(method.getReturnType()));
411 } catch (CannotCompileException e) {
412 throw new RuntimeException(klass.getName() + "." + method.getName(), e);
413 } catch (NotFoundException e) {
414 throw new RuntimeException(klass.getName() + "." + method.getName(), e);
415 }
416 }
417 }
418 }
419
420 private String emptyBodyCode(CtClass returnType) {
421 if (this.stubException == null) {
422 return MakeEmptyMethodInstrumentor.emptyBody(returnType);
423 } else {
424 StringBuffer b = new StringBuffer();
425 b.append("throw new ");
426 b.append(this.stubException);
427 if (this.stubExceptionMessage == null) {
428 b.append("();");
429 } else {
430 b.append("(\"");
431 b.append(this.stubExceptionMessage);
432 b.append("\");");
433 }
434 return b.toString();
435 }
436 }
437
438 private int getModifiers(Node node) {
439 return decodeModifiers(ConfigFileUtil.getNodeAttribute(node, "modifiers"));
440 }
441
442 private int decodeModifiers(String modifiers) {
443 if (modifiers == null) {
444 return 0;
445 }
446 int mod = 0;
447 StringTokenizer st = new StringTokenizer(modifiers, " ");
448 if (st.hasMoreTokens()) {
449 while (st.hasMoreTokens()) {
450 mod |= decodeModifier(st.nextToken());
451 }
452 } else {
453 mod = decodeModifier(modifiers);
454 }
455 return mod;
456 }
457
458 private int decodeModifier(String modifier) {
459 if (modifier.equalsIgnoreCase("public")) {
460 return Modifier.PUBLIC;
461 } else if (modifier.equalsIgnoreCase("protected")) {
462 return Modifier.PROTECTED;
463 } else if (modifier.equalsIgnoreCase("private")) {
464 return Modifier.PRIVATE;
465 } else if (modifier.equalsIgnoreCase("abstract")) {
466 return Modifier.ABSTRACT;
467 } else if (modifier.equalsIgnoreCase("static")) {
468 return Modifier.STATIC;
469 } else if (modifier.equalsIgnoreCase("final")) {
470 return Modifier.FINAL;
471 } else if (modifier.equalsIgnoreCase("volatile")) {
472 return Modifier.TRANSIENT;
473 } else if (modifier.equalsIgnoreCase("synchronized")) {
474 return Modifier.SYNCHRONIZED;
475 } else if (modifier.equalsIgnoreCase("native")) {
476 return Modifier.NATIVE;
477 } else if (modifier.equalsIgnoreCase("interface")) {
478 return Modifier.INTERFACE;
479 } else if (modifier.equalsIgnoreCase("strictfp")) {
480 return Modifier.STRICT;
481 } else {
482 throw new RuntimeException("Invalid modifier [" + modifier + "]");
483 }
484 }
485
486 private void loadExceptions(CtBehavior m, Node node) {
487 Node[] list = ConfigFileUtil.getChildNodes(node, "exception");
488 if (list.length == 0) {
489 return;
490 }
491 CtClass[] types = new CtClass[list.length];
492 for (int i = 0; i < list.length; i++) {
493 types[i] = createClass(ConfigFileUtil.getNodeAttribute(list[i], "name"));
494 }
495 try {
496 m.setExceptionTypes(types);
497 } catch (NotFoundException e) {
498 throw new RuntimeException("Can't add exceptions", e);
499 }
500 }
501
502 private void loadMethods(CtClass klass, Node node) {
503 Node[] list = ConfigFileUtil.getChildNodes(node, "method");
504 for (int i = 0; i < list.length; i++) {
505 int modifiers = getModifiers(list[i]);
506 if (!this.filter.isAPIModifier(modifiers)) {
507 continue;
508 }
509 String mname = ConfigFileUtil.getNodeAttribute(list[i], "name");
510 CtClass[] parameters = getParameters(list[i]);
511 CtClass returnType = createInterface(ConfigFileUtil.getNodeAttribute(list[i], "return"));
512 CtMethod method = new CtMethod(returnType, mname, parameters, klass);
513 method.setModifiers(modifiers);
514 try {
515
516
517
518
519
520 loadExceptions(method, list[i]);
521 klass.addMethod(method);
522 } catch (CannotCompileException e) {
523 throw new RuntimeException(klass.getName(), e);
524 }
525 }
526 }
527
528 private void loadFields(CtClass klass, Node node) {
529 Node[] list = ConfigFileUtil.getChildNodes(node, "field");
530 for (int i = 0; i < list.length; i++) {
531 int modifiers = getModifiers(list[i]);
532 if (!this.filter.isAPIModifier(modifiers)) {
533 continue;
534 }
535 String fname = ConfigFileUtil.getNodeAttribute(list[i], "name");
536 CtClass fieldType = createInterface(ConfigFileUtil.getNodeAttribute(list[i], "type"));
537 CtField field;
538 try {
539 field = new CtField(fieldType, fname, klass);
540 field.setModifiers(modifiers);
541 CtField.Initializer initializer = null;
542 String constValue = ConfigFileUtil.getNodeAttribute(list[i], "constant-value");
543 if (constValue != null) {
544 initializer = createFieldInitializer(fieldType, constValue, klass.getName() + "." + fname);
545 }
546 klass.addField(field, initializer);
547 } catch (CannotCompileException e) {
548 throw new RuntimeException(klass.getName() + " filed " + fname, e);
549 }
550 }
551 }
552
553 private CtField.Initializer createFieldInitializer(CtClass fieldType, String constValue, String name) {
554 if (APIFilter.javaLangString.equals(fieldType.getName())) {
555 return CtField.Initializer.constant(constValue);
556 } else if (fieldType == CtClass.longType) {
557 return CtField.Initializer.constant(Long.valueOf(constValue).longValue());
558 } else if (fieldType == CtClass.floatType) {
559 return CtField.Initializer.constant(Float.valueOf(constValue).floatValue());
560 } else if (fieldType == CtClass.doubleType) {
561 return CtField.Initializer.constant(Double.valueOf(constValue).doubleValue());
562 } else if (fieldType == CtClass.booleanType) {
563 return CtField.Initializer.constant(Boolean.valueOf(constValue).booleanValue());
564 } else {
565 return CtField.Initializer.constant(Integer.valueOf(constValue).intValue());
566 }
567 }
568 }