1. /**
  2. * Copyright: Copyright (c) 2002-2004
  3. * Company: JavaResearch(http://www.javaresearch.org)
  4. */
  5. package org.jr.java2html;
  6. import java.io.BufferedReader;
  7. import java.io.File;
  8. import java.io.FileReader;
  9. import java.net.URL;
  10. import java.util.HashMap;
  11. import java.util.Iterator;
  12. import java.util.ArrayList;
  13. import org.jr.util.StringUtil;
  14. /**
  15. * 根据类名得到相应的链接的工具类。 根据以下的规则得到类名是否有对应的源代码:
  16. * <ol>
  17. * <li>如果类名前有包名,则根据这个全限定名进行匹配
  18. * <li>如果导入语句中有以该类名结尾的导入语句,则以该导入声明的全限定名进行匹配
  19. * <li>如果导入语句中有带*号的通用导入,则以该包加类名逐个进行匹配
  20. * <li>根据类文件所在的路径加类名进行匹配
  21. * <li>以java.lang作为包名进行匹配
  22. * </ol>
  23. * 最后更新日期:2004年11月30日
  24. * @author cherami@javaresearch.org
  25. * @version 0.9
  26. */
  27. public class ClassLink {
  28. HashMap linkMap;
  29. /**
  30. * 构造一个空的ClassLink对象。
  31. */
  32. public ClassLink() {
  33. }
  34. /**
  35. * 以指定参数构造一个ClassLink对象。
  36. * @param linkMap 类名HashMap和URL映射的集合
  37. */
  38. public ClassLink(HashMap linkMap) {
  39. this.linkMap = linkMap;
  40. }
  41. /**
  42. * 将指定的类名HashMap和链接添加到映射中。
  43. * @param classMap 类名HashMap
  44. * @param link 类的URL
  45. */
  46. public void put(HashMap classMap, String link) {
  47. if (linkMap == null) {
  48. linkMap = new HashMap();
  49. }
  50. linkMap.put(classMap, link);
  51. }
  52. /**
  53. * 将指定的ClassLink对象中的所有映射添加到当前的映射中。
  54. * @param classLink ClassLink对象
  55. */
  56. public void putAll(ClassLink classLink) {
  57. if (linkMap == null) {
  58. linkMap = new HashMap();
  59. }
  60. linkMap.putAll(classLink.linkMap);
  61. }
  62. /**
  63. * 根据指定的类名从当前映射中取得类名对应的URL。
  64. * @param className 当前解析的类名
  65. * @param packagePath 当前解析的文件的包名
  66. * @param classPackage 当前解析的类的限定包名
  67. * @param importedClasses 当前解析的文件的导入声明
  68. * @return 类名对应的URL,如果不存在返回null。
  69. */
  70. public String getLink(String className, String packagePath,
  71. String classPackage, ArrayList importedClasses) {
  72. if (linkMap == null || linkMap.size() == 0) {
  73. return null;
  74. }
  75. Iterator classMaps = linkMap.keySet().iterator();
  76. while (classMaps.hasNext()) {
  77. HashMap classMap = (HashMap) classMaps.next();
  78. Object path = classMap.get(className);
  79. if (path != null) {
  80. String root = (String) linkMap.get(classMap);
  81. String classPath = getPath(path, className, packagePath, classPackage,
  82. importedClasses);
  83. if (classPath == null) {
  84. return null;
  85. } else {
  86. return getRoot(root, packagePath) + classPath;
  87. }
  88. }
  89. }
  90. return null;
  91. }
  92. /**
  93. * 根据指定的根路径和包路径得到实际的根路径。
  94. * 如果指定的根路径是空字符串说明是当前转换的项目的类,实际的路径将是根据指定的包路径得到的相对路径;
  95. * 如果指定的根路径不是以http://或者file:///开头的绝对URL路径说明是本地相对路径,实际的路径将是根据指定的包路径得到的相对路径加指定的根路径;
  96. * 如果是其它则说明是绝对的URL路径,直接返回指定的根路径。
  97. * @param root 根路径
  98. * @param packagePath 当前解析的文件的包路径
  99. * @return 实际的根路径。
  100. */
  101. private String getRoot(String root, String packagePath) {
  102. if (root.equals("")) {
  103. if (packagePath != null) {
  104. int count = StringUtil.getSubtringCount(packagePath, "/");
  105. if (packagePath.length() > 0) {
  106. count++;
  107. }
  108. if (count > 0) {
  109. return Utility.fill("../", count);
  110. }
  111. return "";
  112. } else {
  113. return root;
  114. }
  115. } else if (!root.startsWith("http://") && !root.startsWith("file:///")) {
  116. if (packagePath != null) {
  117. int count = StringUtil.getSubtringCount(packagePath, "/");
  118. if (packagePath.length() > 0) {
  119. count++;
  120. }
  121. if (count > 0) {
  122. return Utility.fill("../", count) + root;
  123. }
  124. return root;
  125. } else {
  126. return root;
  127. }
  128. } else {
  129. return root;
  130. }
  131. }
  132. /**
  133. * 根据直接取得的可能路径对象得到完全匹配的类路径。
  134. * @param path 可能路径,String类型或者ArrayList类型
  135. * @param className 当前解析的类名
  136. * @param packagePath 当前解析的文件的包名
  137. * @param classPackage 当前解析的类的限定包名
  138. * @param importedClasses 当前解析的文件的导入声明
  139. * @return 完全匹配的类路径。
  140. */
  141. private String getPath(Object path, String className, String packagePath,
  142. String classPackage, ArrayList importedClasses) {
  143. //如果只有一个匹配的路径
  144. if (path instanceof String) {
  145. //如果类名前有限定包名则比较得到的路径是否是以该限定包名开头的,是则返回该路径,否则返回null
  146. if (classPackage != null) {
  147. if (((String) path).startsWith(classPackage)) {
  148. return ((String) path);
  149. }
  150. return null;
  151. }
  152. //如果文件中有包导入声明
  153. if (importedClasses.size() > 0) {
  154. //取完全匹配类名的导入声明和路径中完全匹配的
  155. for (int i = 0; i < importedClasses.size(); i++) {
  156. String currentPackage = (String) importedClasses.get(i);
  157. if (currentPackage.endsWith("/" + className)) {
  158. if (((String) path).startsWith(currentPackage)) {
  159. return ((String) path);
  160. }
  161. break;
  162. }
  163. }
  164. //取通配形式的导入声明和路径中完全匹配的
  165. for (int i = 0; i < importedClasses.size(); i++) {
  166. String currentPackage = (String) importedClasses.get(i);
  167. if (currentPackage.endsWith("/")) {
  168. String fullName = currentPackage + className;
  169. if (((String) path).startsWith(fullName)) {
  170. return ((String) path);
  171. }
  172. }
  173. }
  174. }
  175. //当前文件的包名加类名是否匹配
  176. if (packagePath != null) {
  177. if (((String) path).startsWith(packagePath)) {
  178. return ((String) path);
  179. }
  180. }
  181. //java中默认导入的包java/lang/是否匹配
  182. if (((String) path).startsWith("java/lang/")) {
  183. return ((String) path);
  184. }
  185. //没有匹配
  186. return null;
  187. } else {//有多个可能的匹配路径
  188. ArrayList pathList = (ArrayList) path;
  189. //如果类名前有限定包名则查找是否有完全匹配的
  190. if (classPackage != null) {
  191. String fullName = classPackage + className;
  192. for (int j = 0; j < pathList.size(); j++) {
  193. String currentPath = (String) pathList.get(j);
  194. if (currentPath.startsWith(fullName)) {
  195. return currentPath;
  196. }
  197. }
  198. return null;
  199. }
  200. //如果有导入声明
  201. if (importedClasses.size() > 0) {
  202. //查找导入声明中可以完全匹配的
  203. for (int i = 0; i < importedClasses.size(); i++) {
  204. String currentPackage = (String) importedClasses.get(i);
  205. if (currentPackage.endsWith("/" + className)) {
  206. for (int j = 0; j < pathList.size(); j++) {
  207. String currentPath = (String) pathList.get(j);
  208. if (currentPath.startsWith(currentPackage)) {
  209. return currentPath;
  210. }
  211. }
  212. break;
  213. }
  214. }
  215. //查找导入声明中通配中可以完全匹配的
  216. for (int i = 0; i < importedClasses.size(); i++) {
  217. String currentPackage = (String) importedClasses.get(i);
  218. if (currentPackage.endsWith("/")) {
  219. String fullName = currentPackage + className;
  220. for (int j = 0; j < pathList.size(); j++) {
  221. String currentPath = (String) pathList.get(j);
  222. if (currentPath.startsWith(fullName)) {
  223. return currentPath;
  224. }
  225. }
  226. }
  227. }
  228. }
  229. //用文件包名加类名进行完全匹配
  230. String fullName = packagePath + "/" + className;
  231. for (int j = 0; j < pathList.size(); j++) {
  232. String currentPath = (String) pathList.get(j);
  233. if (currentPath.startsWith(fullName)) {
  234. return currentPath;
  235. }
  236. }
  237. //用java缺省导入的java/lang包进行完全匹配
  238. fullName = "java/lang/" + className;
  239. for (int j = 0; j < pathList.size(); j++) {
  240. String currentPath = (String) pathList.get(j);
  241. if (currentPath.startsWith(fullName)) {
  242. return currentPath;
  243. }
  244. }
  245. return null;
  246. }
  247. }
  248. /**
  249. * 从指定文件中取得类名URL映射关系。
  250. * @param mapFilename 类名URL映射关系文件
  251. * @return 类名URL映射关系对象
  252. */
  253. public static ClassLink getClassLink(String mapFilename) {
  254. try {
  255. BufferedReader buffer = new BufferedReader(new FileReader(mapFilename));
  256. String line = buffer.readLine();
  257. ClassLink classLink = new ClassLink();
  258. while (line != null) {
  259. if (line.startsWith("#") || line.length() == 0) {
  260. line = buffer.readLine();
  261. continue;
  262. }
  263. String[] array = line.split("=");
  264. String path;
  265. String link;
  266. URL url;
  267. if (array.length == 1) {
  268. path = line;
  269. int index = line.lastIndexOf("/");
  270. if (index >= 0) {
  271. link = line.substring(0, index + 1);
  272. } else {
  273. link = "";
  274. }
  275. } else {
  276. path = array[0];
  277. link = array[1];
  278. }
  279. url = getURL(path);
  280. HashMap map = (HashMap) org.jr.io.FileReader.readObject(url);
  281. if (map != null) {
  282. classLink.put(map, link);
  283. }
  284. line = buffer.readLine();
  285. }
  286. return classLink;
  287. }
  288. catch (Exception e) {
  289. e.printStackTrace();
  290. }
  291. return null;
  292. }
  293. /**
  294. * 根据指定的路径得到路径的URL。
  295. * 如果路径是以http://或者file:///开头的则直接生成URL, 否则将路径转换为文件对象后再转换为URL。
  296. * @param path 路径
  297. * @return 路径对应的URL
  298. */
  299. private static URL getURL(String path) {
  300. try {
  301. if (path.startsWith("http://") || path.startsWith("file:///")) {
  302. return new URL(path);
  303. } else {
  304. return new File(path).toURL();
  305. }
  306. }
  307. catch (Exception e) {
  308. e.printStackTrace();
  309. return null;
  310. }
  311. }
  312. }