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