    001    /**
    002     *      jline - Java console input library
    003     *      Copyright (c) 2002, 2003, 2004, 2005, Marc Prud'hommeaux <mwp1@cornell.edu>
    004     *      All rights reserved.
    005     *
    006     *      Redistribution and use in source and binary forms, with or
    007     *      without modification, are permitted provided that the following
    008     *      conditions are met:
    009     *
    010     *      Redistributions of source code must retain the above copyright
    011     *      notice, this list of conditions and the following disclaimer.
    012     *
    013     *      Redistributions in binary form must reproduce the above copyright
    014     *      notice, this list of conditions and the following disclaimer
    015     *      in the documentation and/or other materials provided with
    016     *      the distribution.
    017     *
    018     *      Neither the name of JLine nor the names of its contributors
    019     *      may be used to endorse or promote products derived from this
    020     *      software without specific prior written permission.
    021     *
    022     *      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    023     *      "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
    024     *      BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
    025     *      AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
    026     *      EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
    027     *      FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
    028     *      OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    029     *      PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    030     *      DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
    031     *      AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    032     *      LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
    033     *      IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
    034     *      OF THE POSSIBILITY OF SUCH DAMAGE.
    035     */
    036    package jline;
    037
    038    import java.io.*;
    039    import java.net.*;
    040    import java.util.*;
    041    import java.util.jar.JarFile;
    042    import java.util.jar.JarEntry;
    043
    044
    045    /**
    046     *  A Completor implementation that completes java class names. By default,
    047     *  it scans the java class path to locate all the classes.
    048     *
    049     *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
    050     */
    051    public class ClassNameCompletor
    052            extends SimpleCompletor
    053    {
    054            /**
    055             *  Complete candidates using all the classes available in the
    056             *  java <em>CLASSPATH</em>.
    057             */
    058            public ClassNameCompletor ()
    059                    throws IOException
    060            {
    061                    this (null);
    062            }
    063
    064
    065            public ClassNameCompletor (final SimpleCompletorFilter filter)
    066                    throws IOException
    067            {
    068                    super (getClassNames (), filter);
    069                    setDelimiter (".");
    070            }
    071
    072
    073            public static String[] getClassNames ()
    074                    throws IOException
    075            {
    076                    Set urls = new HashSet ();
    077                    for (ClassLoader loader = ClassNameCompletor.class.getClassLoader ();
    078                            loader != null; loader = loader.getParent ())
    079                    {
    080                            if (!(loader instanceof URLClassLoader))
    081                                    continue;
    082
    083                            urls.addAll (Arrays.asList (((URLClassLoader)loader).getURLs ()));
    084                    }
    085
    086                    // Now add the URL that holds java.lang.String. This is because
    087                    // some JVMs do not report the core classes jar in the list of
    088                    // class loaders.
    089                    Class[] systemClasses = new Class[] {
    090                            String.class,
    091                            javax.swing.JFrame.class
    092                            };
    093                    for (int i = 0; i < systemClasses.length; i++)
    094                    {
    095                            URL classURL = systemClasses[i].getResource ("/"
    096                                    + systemClasses[i].getName ().replace ('.', '/') + ".class");
    097                            if (classURL != null)
    098                            {
    099                                    URLConnection uc = (URLConnection)classURL.openConnection ();
    100                                    if (uc instanceof JarURLConnection)
    101                                            urls.add (((JarURLConnection)uc).getJarFileURL ());
    102                            }
    103                    }
    104
    105
    106                    Set classes = new HashSet ();
    107                    for (Iterator i = urls.iterator (); i.hasNext (); )
    108                    {
    109                            URL url = (URL)i.next ();
    110                            File file = new File (url.getFile ());
    111                            if (file.isDirectory ())
    112                            {
    113                                    Set files = getClassFiles (file.getAbsolutePath (),
    114                                            new HashSet (), file, new int[] { 200 });
    115                                    classes.addAll (files);
    116                                    continue;
    117                            }
    118
    119                            if (file == null || !file.isFile ()) // TODO: handle directories
    120                                    continue;
    121
    122                            JarFile jf = new JarFile (file);
    123                            for (Enumeration entries = jf.entries ();
    124                                    entries.hasMoreElements () ;)
    125                            {
    126                                    JarEntry entry = (JarEntry)entries.nextElement ();
    127                                    if (entry == null)
    128                                            continue;
    129
    130                                    String name = entry.getName ();
    131                                    if (!name.endsWith (".class")) // only use class files
    132                                            continue;
    133
    134                                    classes.add (name);
    135                            }
    136                    }
    137
    138                    // now filter classes by changing "/" to "." and trimming the
    139                    // trailing ".class"
    140                    Set classNames = new TreeSet ();
    141                    for (Iterator i = classes.iterator (); i.hasNext (); )
    142                    {
    143                            String name = (String)i.next ();
    144                            classNames.add (name.replace ('/', '.').substring (0,
    145                                    name.length () - 6));
    146                    }
    147
    148                    return (String[])classNames.toArray (new String[classNames.size ()]);
    149            }
    150
    151
    152            private static Set getClassFiles (String root, Set holder, File directory,
    153                    int[] maxDirectories)
    154            {
    155                    // we have passed the maximum number of directories to scan
    156                    if (maxDirectories[0]-- < 0)
    157                            return holder;
    158
    159                    File[] files = directory.listFiles ();
    160                    for (int i = 0; files != null && i < files.length; i++)
    161                    {
    162                            String name = files[i].getAbsolutePath ();
    163                            if (!(name.startsWith (root)))
    164                                    continue;
    165                            else if (files[i].isDirectory ())
    166                                    getClassFiles (root, holder, files[i], maxDirectories);
    167                            else if (files[i].getName ().endsWith (".class"))
    168                                    holder.add (files[i].getAbsolutePath ().substring (
    169                                            root.length () + 1));
    170                    }
    171
    172                    return holder;
    173            }
    174    }
    175