001 /**
002 * jline - Java console input library
003 * Copyright (c) 2002,2003 Marc Prud'hommeaux mwp1@cornell.edu
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either
008 * version 2.1 of the License, or (at your option) any later version.
009 *
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013 * Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public
016 * License along with this library; if not, write to the Free Software
017 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018 */
019 package jline;
020
021 import java.io.*;
022 import java.net.*;
023 import java.util.*;
024 import java.util.jar.JarFile;
025 import java.util.jar.JarEntry;
026
027
028 /**
029 * A Completor implementation that completes java class names. By default,
030 * it scans the java class path to locate all the classes.
031 *
032 * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
033 */
034 public class ClassNameCompletor
035 extends SimpleCompletor
036 {
037 /**
038 * Complete candidates using all the classes available in the
039 * java <em>CLASSPATH</em>.
040 */
041 public ClassNameCompletor ()
042 throws IOException
043 {
044 this (null);
045 }
046
047
048 public ClassNameCompletor (final SimpleCompletorFilter filter)
049 throws IOException
050 {
051 super (getClassNames (), filter);
052 setDelimiter (".");
053 }
054
055
056 public static String[] getClassNames ()
057 throws IOException
058 {
059 Set urls = new HashSet ();
060 for (ClassLoader loader = ClassNameCompletor.class.getClassLoader ();
061 loader != null; loader = loader.getParent ())
062 {
063 if (!(loader instanceof URLClassLoader))
064 continue;
065
066 urls.addAll (Arrays.asList (((URLClassLoader)loader).getURLs ()));
067 }
068
069 // Now add the URL that holds java.lang.String. This is because
070 // some JVMs do not report the core classes jar in the list of
071 // class loaders.
072 Class[] systemClasses = new Class[] {
073 String.class,
074 javax.swing.JFrame.class
075 };
076 for (int i = 0; i < systemClasses.length; i++)
077 {
078 URL classURL = systemClasses[i].getResource ("/"
079 + systemClasses[i].getName ().replace ('.', '/') + ".class");
080 if (classURL != null)
081 {
082 URLConnection uc = (URLConnection)classURL.openConnection ();
083 if (uc instanceof JarURLConnection)
084 urls.add (((JarURLConnection)uc).getJarFileURL ());
085 }
086 }
087
088
089 Set classes = new HashSet ();
090 for (Iterator i = urls.iterator (); i.hasNext (); )
091 {
092 URL url = (URL)i.next ();
093 File file = new File (url.getFile ());
094 if (file.isDirectory ())
095 {
096 Set files = getClassFiles (file.getAbsolutePath (),
097 new HashSet (), file, new int[] { 200 });
098 classes.addAll (files);
099 continue;
100 }
101
102 if (file == null || !file.isFile ()) // TODO: handle directories
103 continue;
104
105 JarFile jf = new JarFile (file);
106 for (Enumeration entries = jf.entries ();
107 entries.hasMoreElements () ;)
108 {
109 JarEntry entry = (JarEntry)entries.nextElement ();
110 if (entry == null)
111 continue;
112
113 String name = entry.getName ();
114 if (!name.endsWith (".class")) // only use class files
115 continue;
116
117 classes.add (name);
118 }
119 }
120
121 // now filter classes by changing "/" to "." and trimming the
122 // trailing ".class"
123 Set classNames = new TreeSet ();
124 for (Iterator i = classes.iterator (); i.hasNext (); )
125 {
126 String name = (String)i.next ();
127 classNames.add (name.replace ('/', '.').substring (0,
128 name.length () - 6));
129 }
130
131 return (String[])classNames.toArray (new String[classNames.size ()]);
132 }
133
134
135 private static Set getClassFiles (String root, Set holder, File directory,
136 int[] maxDirectories)
137 {
138 // we have passed the maximum number of directories to scan
139 if (maxDirectories[0]-- < 0)
140 return holder;
141
142 File[] files = directory.listFiles ();
143 for (int i = 0; files != null && i < files.length; i++)
144 {
145 String name = files[i].getAbsolutePath ();
146 if (!(name.startsWith (root)))
147 continue;
148 else if (files[i].isDirectory ())
149 getClassFiles (root, holder, files[i], maxDirectories);
150 else if (files[i].getName ().endsWith (".class"))
151 holder.add (files[i].getAbsolutePath ().substring (
152 root.length () + 1));
153 }
154
155 return holder;
156 }
157 }
158