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