ClassNameCompletor.java revision 3aa4d1f0c4f24e2d2788ec9886de8a5ca3cd806b
1/* 2 * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. 3 * 4 * This software is distributable under the BSD license. See the terms of the 5 * BSD license in the documentation provided with this software. 6 */ 7package jline; 8 9import java.io.*; 10import java.net.*; 11import java.util.*; 12import java.util.jar.JarEntry; 13import java.util.jar.JarFile; 14 15/** 16 * A Completor implementation that completes java class names. By default, 17 * it scans the java class path to locate all the classes. 18 * 19 * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> 20 */ 21public class ClassNameCompletor extends SimpleCompletor { 22 23 /** 24 * Complete candidates using all the classes available in the 25 * java <em>CLASSPATH</em>. 26 */ 27 public ClassNameCompletor() throws IOException { 28 this(null); 29 } 30 31 public ClassNameCompletor(final SimpleCompletorFilter filter) 32 throws IOException { 33 super(getClassNames(), filter); 34 setDelimiter("."); 35 } 36 37 public static String[] getClassNames() throws IOException { 38 Set urls = new HashSet(); 39 40 for (ClassLoader loader = ClassNameCompletor.class 41 .getClassLoader(); loader != null; 42 loader = loader.getParent()) { 43 if (!(loader instanceof URLClassLoader)) { 44 continue; 45 } 46 47 urls.addAll(Arrays.asList(((URLClassLoader) loader).getURLs())); 48 } 49 50 // Now add the URL that holds java.lang.String. This is because 51 // some JVMs do not report the core classes jar in the list of 52 // class loaders. 53 Class[] systemClasses = new Class[] { 54 String.class, javax.swing.JFrame.class 55 }; 56 57 for (int i = 0; i < systemClasses.length; i++) { 58 URL classURL = systemClasses[i].getResource("/" 59 + systemClasses[i].getName() .replace('.', '/') + ".class"); 60 61 if (classURL != null) { 62 URLConnection uc = (URLConnection) classURL.openConnection(); 63 64 if (uc instanceof JarURLConnection) { 65 urls.add(((JarURLConnection) uc).getJarFileURL()); 66 } 67 } 68 } 69 70 Set classes = new HashSet(); 71 72 for (Iterator i = urls.iterator(); i.hasNext();) { 73 URL url = (URL) i.next(); 74 File file = new File(url.getFile()); 75 76 if (file.isDirectory()) { 77 Set files = getClassFiles(file.getAbsolutePath(), 78 new HashSet(), file, new int[] { 200 }); 79 classes.addAll(files); 80 81 continue; 82 } 83 84 if ((file == null) || !file.isFile()) // TODO: handle directories 85 { 86 continue; 87 } 88 if (!file.toString().endsWith (".jar")) 89 continue; 90 91 JarFile jf = new JarFile(file); 92 93 for (Enumeration e = jf.entries(); e.hasMoreElements();) { 94 JarEntry entry = (JarEntry) e.nextElement(); 95 96 if (entry == null) { 97 continue; 98 } 99 100 String name = entry.getName(); 101 102 if (!name.endsWith(".class")) // only use class files 103 { 104 continue; 105 } 106 107 classes.add(name); 108 } 109 } 110 111 // now filter classes by changing "/" to "." and trimming the 112 // trailing ".class" 113 Set classNames = new TreeSet(); 114 115 for (Iterator i = classes.iterator(); i.hasNext();) { 116 String name = (String) i.next(); 117 classNames.add(name.replace('/', '.'). 118 substring(0, name.length() - 6)); 119 } 120 121 return (String[]) classNames.toArray(new String[classNames.size()]); 122 } 123 124 private static Set getClassFiles(String root, Set holder, File directory, 125 int[] maxDirectories) { 126 // we have passed the maximum number of directories to scan 127 if (maxDirectories[0]-- < 0) { 128 return holder; 129 } 130 131 File[] files = directory.listFiles(); 132 133 for (int i = 0; (files != null) && (i < files.length); i++) { 134 String name = files[i].getAbsolutePath(); 135 136 if (!(name.startsWith(root))) { 137 continue; 138 } else if (files[i].isDirectory()) { 139 getClassFiles(root, holder, files[i], maxDirectories); 140 } else if (files[i].getName().endsWith(".class")) { 141 holder.add(files[i].getAbsolutePath(). 142 substring(root.length() + 1)); 143 } 144 } 145 146 return holder; 147 } 148} 149