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