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 }