001 /* 002 * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. 003 * 004 * This software is distributable under the BSD license. See the terms of the 005 * BSD license in the documentation provided with this software. 006 */ 007 package jline; 008 009 import java.io.*; 010 import java.net.*; 011 import java.util.*; 012 import java.util.jar.JarEntry; 013 import java.util.jar.JarFile; 014 015 /** 016 * A Completor implementation that completes java class names. By default, 017 * it scans the java class path to locate all the classes. 018 * 019 * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> 020 */ 021 public class ClassNameCompletor extends SimpleCompletor { 022 023 /** 024 * Complete candidates using all the classes available in the 025 * java <em>CLASSPATH</em>. 026 */ 027 public ClassNameCompletor() throws IOException { 028 this(null); 029 } 030 031 public ClassNameCompletor(final SimpleCompletorFilter filter) 032 throws IOException { 033 super(getClassNames(), filter); 034 setDelimiter("."); 035 } 036 037 public static String[] getClassNames() throws IOException { 038 Set urls = new HashSet(); 039 040 for (ClassLoader loader = ClassNameCompletor.class 041 .getClassLoader(); loader != null; 042 loader = loader.getParent()) { 043 if (!(loader instanceof URLClassLoader)) { 044 continue; 045 } 046 047 urls.addAll(Arrays.asList(((URLClassLoader) loader).getURLs())); 048 } 049 050 // Now add the URL that holds java.lang.String. This is because 051 // some JVMs do not report the core classes jar in the list of 052 // class loaders. 053 Class[] systemClasses = new Class[] { 054 String.class, javax.swing.JFrame.class 055 }; 056 057 for (int i = 0; i < systemClasses.length; i++) { 058 URL classURL = systemClasses[i].getResource("/" 059 + systemClasses[i].getName() .replace('.', '/') + ".class"); 060 061 if (classURL != null) { 062 URLConnection uc = (URLConnection) classURL.openConnection(); 063 064 if (uc instanceof JarURLConnection) { 065 urls.add(((JarURLConnection) uc).getJarFileURL()); 066 } 067 } 068 } 069 070 Set classes = new HashSet(); 071 072 for (Iterator i = urls.iterator(); i.hasNext();) { 073 URL url = (URL) i.next(); 074 File file = new File(url.getFile()); 075 076 if (file.isDirectory()) { 077 Set files = getClassFiles(file.getAbsolutePath(), 078 new HashSet(), file, new int[] { 200 }); 079 classes.addAll(files); 080 081 continue; 082 } 083 084 if ((file == null) || !file.isFile()) // TODO: handle directories 085 { 086 continue; 087 } 088 089 JarFile jf = new JarFile(file); 090 091 for (Enumeration e = jf.entries(); e.hasMoreElements();) { 092 JarEntry entry = (JarEntry) e.nextElement(); 093 094 if (entry == null) { 095 continue; 096 } 097 098 String name = entry.getName(); 099 100 if (!name.endsWith(".class")) // only use class files 101 { 102 continue; 103 } 104 105 classes.add(name); 106 } 107 } 108 109 // now filter classes by changing "/" to "." and trimming the 110 // trailing ".class" 111 Set classNames = new TreeSet(); 112 113 for (Iterator i = classes.iterator(); i.hasNext();) { 114 String name = (String) i.next(); 115 classNames.add(name.replace('/', '.'). 116 substring(0, name.length() - 6)); 117 } 118 119 return (String[]) classNames.toArray(new String[classNames.size()]); 120 } 121 122 private static Set getClassFiles(String root, Set holder, File directory, 123 int[] maxDirectories) { 124 // we have passed the maximum number of directories to scan 125 if (maxDirectories[0]-- < 0) { 126 return holder; 127 } 128 129 File[] files = directory.listFiles(); 130 131 for (int i = 0; (files != null) && (i < files.length); i++) { 132 String name = files[i].getAbsolutePath(); 133 134 if (!(name.startsWith(root))) { 135 continue; 136 } else if (files[i].isDirectory()) { 137 getClassFiles(root, holder, files[i], maxDirectories); 138 } else if (files[i].getName().endsWith(".class")) { 139 holder.add(files[i].getAbsolutePath(). 140 substring(root.length() + 1)); 141 } 142 } 143 144 return holder; 145 } 146 }