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