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