001 /**
002 * jline - Java console input library
003 * Copyright (c) 2002-2006, 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