1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant.util.depend.bcel;
  18. import java.util.Enumeration;
  19. import java.util.Hashtable;
  20. import java.util.StringTokenizer;
  21. import org.apache.bcel.classfile.ConstantClass;
  22. import org.apache.bcel.classfile.ConstantPool;
  23. import org.apache.bcel.classfile.EmptyVisitor;
  24. import org.apache.bcel.classfile.Field;
  25. import org.apache.bcel.classfile.JavaClass;
  26. import org.apache.bcel.classfile.Method;
  27. import org.apache.bcel.classfile.ConstantNameAndType;
  28. /**
  29. * A BCEL visitor implementation to collect class dependency information
  30. *
  31. */
  32. public class DependencyVisitor extends EmptyVisitor {
  33. /** The collectd dependencies */
  34. private Hashtable dependencies = new Hashtable();
  35. /**
  36. * The current class's constant pool - used to determine class names
  37. * from class references.
  38. */
  39. private ConstantPool constantPool;
  40. /**
  41. * Get the dependencies collected by this visitor
  42. *
  43. * @return a Enumeration of classnames, being the classes upon which the
  44. * visited classes depend.
  45. */
  46. public Enumeration getDependencies() {
  47. return dependencies.keys();
  48. }
  49. /** Clear the curretn set of collected dependencies. */
  50. public void clearDependencies() {
  51. dependencies.clear();
  52. }
  53. /**
  54. * Visit the constant pool of a class
  55. *
  56. * @param constantPool the constant pool of the class being visited.
  57. */
  58. public void visitConstantPool(ConstantPool constantPool) {
  59. this.constantPool = constantPool;
  60. }
  61. /**
  62. * Visit a class reference
  63. *
  64. * @param constantClass the constantClass entry for the class reference
  65. */
  66. public void visitConstantClass(ConstantClass constantClass) {
  67. String classname
  68. = constantClass.getConstantValue(constantPool).toString();
  69. addSlashClass(classname);
  70. }
  71. /**
  72. * Visit a name and type ref
  73. *
  74. * Look for class references in this
  75. *
  76. * @param obj the name and type reference being visited.
  77. */
  78. public void visitConstantNameAndType(ConstantNameAndType obj) {
  79. String name = obj.getName(constantPool);
  80. if (obj.getSignature(constantPool).equals("Ljava/lang/Class;")
  81. && name.startsWith("class$")) {
  82. String classname = name.substring(6).replace('$', '.');
  83. // does the class have a package structure
  84. int index = classname.lastIndexOf(".");
  85. if (index > 0) {
  86. char start;
  87. // check if the package structure is more than 1 level deep
  88. int index2 = classname.lastIndexOf(".", index - 1);
  89. if (index2 != -1) {
  90. // class name has more than 1 package level 'com.company.Class'
  91. start = classname.charAt(index2 + 1);
  92. } else {
  93. // class name has only 1 package level 'package.Class'
  94. start = classname.charAt(0);
  95. }
  96. // Check to see if it's an inner class 'com.company.Class$Inner'
  97. if ((start > 0x40) && (start < 0x5B)) {
  98. // first letter of the previous segment of the class name 'Class'
  99. // is upper case ascii. so according to the spec it's an inner class
  100. classname = classname.substring(0, index) + "$"
  101. + classname.substring(index + 1);
  102. addClass(classname);
  103. } else {
  104. // Add the class in dotted notation 'com.company.Class'
  105. addClass(classname);
  106. }
  107. } else {
  108. // Add a class with no package 'Class'
  109. addClass(classname);
  110. }
  111. }
  112. }
  113. /**
  114. * Visit a field of the class.
  115. *
  116. * @param field the field being visited
  117. */
  118. public void visitField(Field field) {
  119. addClasses(field.getSignature());
  120. }
  121. /**
  122. * Visit a Java class
  123. *
  124. * @param javaClass the class being visited.
  125. */
  126. public void visitJavaClass(JavaClass javaClass) {
  127. addClass(javaClass.getClassName());
  128. }
  129. /**
  130. * Visit a method of the current class
  131. *
  132. * @param method the method being visited.
  133. */
  134. public void visitMethod(Method method) {
  135. String signature = method.getSignature();
  136. int pos = signature.indexOf(")");
  137. addClasses(signature.substring(1, pos));
  138. addClasses(signature.substring(pos + 1));
  139. }
  140. /**
  141. * Add a classname to the list of dependency classes
  142. *
  143. * @param classname the class to be added to the list of dependencies.
  144. */
  145. void addClass(String classname) {
  146. dependencies.put(classname, classname);
  147. }
  148. /**
  149. * Add all the classes from a descriptor string.
  150. *
  151. * @param string the descriptor string, being descriptors separated by
  152. * ';' characters.
  153. */
  154. private void addClasses(String string) {
  155. StringTokenizer tokens = new StringTokenizer(string, ";");
  156. while (tokens.hasMoreTokens()) {
  157. String descriptor = tokens.nextToken();
  158. int pos = descriptor.indexOf('L');
  159. if (pos != -1) {
  160. addSlashClass(descriptor.substring(pos + 1));
  161. }
  162. }
  163. }
  164. /**
  165. * Adds a class name in slash format
  166. * (for example org/apache/tools/ant/Main).
  167. *
  168. * @param classname the class name in slash format
  169. */
  170. private void addSlashClass(String classname) {
  171. addClass(classname.replace('/', '.'));
  172. }
  173. }