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.*;
025    
026    
027    /** 
028     *  A Completor implementation that completes java class names. By default,
029     *  it scans the java class path to locate all the classes.
030     *
031     *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
032     */
033    public class ClassNameCompletor
034            extends SimpleCompletor
035    {
036            /** 
037             *  Complete candidates using all the classes available in the
038             *  java <code>CLASSPATH</code>.
039             */
040            public ClassNameCompletor ()
041                    throws IOException
042            {
043                    this (null);
044            }
045    
046    
047            public ClassNameCompletor (SimpleCompletorFilter filter)
048                    throws IOException
049            {
050                    super (getClassNames (), filter);
051                    setDelimiter (".");
052            }
053    
054    
055            public static String[] getClassNames ()
056                    throws IOException
057            {
058                    Set urls = new HashSet ();
059                    for (ClassLoader loader = ClassNameCompletor.class.getClassLoader ();
060                            loader != null; loader = loader.getParent ())
061                    {
062                            if (!(loader instanceof URLClassLoader))
063                                    continue;
064    
065                            urls.addAll (Arrays.asList (((URLClassLoader)loader).getURLs ()));
066                    }
067    
068                    // Now add the URL that holds java.lang.String. This is because
069                    // some JVMs do not report the core classes jar in the list of
070                    // class loaders.
071                    Class[] systemClasses = new Class[] {
072                            String.class,
073                            javax.swing.JFrame.class
074                            };
075                    for (int i = 0; i < systemClasses.length; i++)
076                    {
077                            URL classURL = systemClasses[i].getResource ("/"
078                                    + systemClasses[i].getName ().replace ('.', '/') + ".class");
079                            if (classURL != null)
080                            {
081                                    URLConnection uc = (URLConnection)classURL.openConnection ();
082                                    if (uc instanceof JarURLConnection)
083                                            urls.add (((JarURLConnection)uc).getJarFileURL ());
084                            }
085                    }
086    
087    
088                    Set classes = new HashSet ();
089                    for (Iterator i = urls.iterator (); i.hasNext (); )
090                    {
091                            URL url = (URL)i.next ();
092                            File file = new File (url.getFile ());
093                            if (file.isDirectory ())
094                            {
095                                    Set files = getClassFiles (file.getAbsolutePath (),
096                                            new HashSet (), file, new int[] { 200 });
097                                    classes.addAll (files);
098                                    continue;
099                            }
100    
101                            if (file == null || !file.isFile ()) // TODO: handle directories
102                                    continue;
103    
104                            JarFile jf = new JarFile (file);
105                            for (Enumeration entries = jf.entries ();
106                                    entries.hasMoreElements () ;)
107                            {
108                                    JarEntry entry = (JarEntry)entries.nextElement ();
109                                    if (entry == null)
110                                            continue;
111    
112                                    String name = entry.getName ();
113                                    if (!name.endsWith (".class")) // only use class files
114                                            continue;
115    
116                                    classes.add (name);
117                            }
118                    }
119    
120                    // now filter classes by changing "/" to "." and trimming the
121                    // trailing ".class"
122                    Set classNames = new TreeSet ();
123                    for (Iterator i = classes.iterator (); i.hasNext (); )
124                    {
125                            String name = (String)i.next ();
126                            classNames.add (name.replace ('/', '.').substring (0,
127                                    name.length () - 6));
128                    }
129    
130                    return (String[])classNames.toArray (new String[classNames.size ()]);
131            }
132    
133    
134            private static Set getClassFiles (String root, Set holder, File directory,
135                    int[] maxDirectories)
136            {
137                    // we have passed the maximum number of directories to scan
138                    if (maxDirectories[0]-- < 0)
139                            return holder;
140    
141                    File[] files = directory.listFiles ();
142                    for (int i = 0; files != null && i < files.length; i++)
143                    {
144                            String name = files[i].getAbsolutePath ();
145                            if (!(name.startsWith (root)))
146                                    continue;
147                            else if (files[i].isDirectory ())
148                                    getClassFiles (root, holder, files[i], maxDirectories);
149                            else if (files[i].getName ().endsWith (".class"))
150                                    holder.add (files[i].getAbsolutePath ().substring (
151                                            root.length () + 1));
152                    }
153    
154                    return holder;
155            }
156    }
157