View Javadoc

1   /**
2    *	jline - Java console input library
3    *	Copyright (c) 2002, 2003, 2004, 2005, Marc Prud'hommeaux <mwp1@cornell.edu>
4    *	All rights reserved.
5    *
6    *	Redistribution and use in source and binary forms, with or
7    *	without modification, are permitted provided that the following
8    *	conditions are met:
9    *
10   *	Redistributions of source code must retain the above copyright
11   *	notice, this list of conditions and the following disclaimer.
12   *
13   *	Redistributions in binary form must reproduce the above copyright
14   *	notice, this list of conditions and the following disclaimer
15   *	in the documentation and/or other materials provided with
16   *	the distribution.
17   *
18   *	Neither the name of JLine nor the names of its contributors
19   *	may be used to endorse or promote products derived from this
20   *	software without specific prior written permission.
21   *
22   *	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23   *	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24   *	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
25   *	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
26   *	EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
27   *	FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
28   *	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29   *	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30   *	DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31   *	AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32   *	LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
33   *	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
34   *	OF THE POSSIBILITY OF SUCH DAMAGE.
35   */
36  package jline;
37  
38  import java.io.*;
39  import java.net.*;
40  import java.util.*;
41  import java.util.jar.JarFile;
42  import java.util.jar.JarEntry;
43  
44  
45  /**
46   *  A Completor implementation that completes java class names. By default,
47   *  it scans the java class path to locate all the classes.
48   *
49   *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
50   */
51  public class ClassNameCompletor
52  	extends SimpleCompletor
53  {
54  	/**
55  	 *  Complete candidates using all the classes available in the
56  	 *  java <em>CLASSPATH</em>.
57  	 */
58  	public ClassNameCompletor ()
59  		throws IOException
60  	{
61  		this (null);
62  	}
63  
64  
65  	public ClassNameCompletor (final SimpleCompletorFilter filter)
66  		throws IOException
67  	{
68  		super (getClassNames (), filter);
69  		setDelimiter (".");
70  	}
71  
72  
73  	public static String[] getClassNames ()
74  		throws IOException
75  	{
76  		Set urls = new HashSet ();
77  		for (ClassLoader loader = ClassNameCompletor.class.getClassLoader ();
78  			loader != null; loader = loader.getParent ())
79  		{
80  			if (!(loader instanceof URLClassLoader))
81  				continue;
82  
83  			urls.addAll (Arrays.asList (((URLClassLoader)loader).getURLs ()));
84  		}
85  
86  		// Now add the URL that holds java.lang.String. This is because
87  		// some JVMs do not report the core classes jar in the list of
88  		// class loaders.
89  		Class[] systemClasses = new Class[] {
90  			String.class,
91  			javax.swing.JFrame.class
92  			};
93  		for (int i = 0; i < systemClasses.length; i++)
94  		{
95  			URL classURL = systemClasses[i].getResource ("/"
96  				+ systemClasses[i].getName ().replace ('.', '/') + ".class");
97  			if (classURL != null)
98  			{
99  				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