/* // Licensed to Julian Hyde under one or more contributor license // agreements. See the NOTICE file distributed with this work for // additional information regarding copyright ownership. // // Julian Hyde licenses this file to you under the Modified BSD License // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at: // // http://opensource.org/licenses/BSD-3-Clause */ package sqlline; import java.io.*; import java.net.*; import java.util.*; import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.jline.reader.Candidate; import org.jline.reader.impl.completer.StringsCompleter; import org.jline.utils.AttributedString; /** * An implementation of {@link org.jline.reader.Completer} that completes * java class names. By default, it scans the java class path to locate all the * classes. */ public class ClassNameCompleter extends StringsCompleter { /** * Completes candidates using all the classes available in the * java <em>CLASSPATH</em>. * * @throws IOException on error */ public ClassNameCompleter() throws IOException { super(getClassNames()); candidates.add( new Candidate(AttributedString.stripAnsi("."), ".", null, null, null, null, true)); } public static Set<String> getClassNames() throws IOException { Set<URL> urls = new HashSet<>(); for (ClassLoader loader = ClassNameCompleter.class.getClassLoader(); loader != null; loader = loader.getParent()) { if (!(loader instanceof URLClassLoader)) { continue; } Collections.addAll(urls, ((URLClassLoader) loader).getURLs()); } // Now add the URL that holds java.lang.String. This is because // some JVMs do not report the core classes jar in the list of // class loaders. Class[] systemClasses = { String.class, javax.swing.JFrame.class }; for (Class systemClass : systemClasses) { URL classURL = systemClass.getResource( "/" + systemClass.getName().replace('.', '/') + ".class"); if (classURL != null) { URLConnection uc = classURL.openConnection(); if (uc instanceof JarURLConnection) { urls.add(((JarURLConnection) uc).getJarFileURL()); } } } Set<String> classes = new HashSet<>(); for (URL url : urls) { File file = new File(URLDecoder.decode(url.getFile(), "UTF-8")); if (file.isDirectory()) { Set<String> files = getClassFiles(file.getAbsolutePath(), new HashSet<>(), file, new int[]{200}); classes.addAll(files); continue; } if (!file.isFile()) { // TODO: handle directories continue; } if (!file.toString().endsWith(".jar")) { continue; } JarFile jf = new JarFile(file); for (Enumeration<JarEntry> e = jf.entries(); e.hasMoreElements();) { JarEntry entry = e.nextElement(); if (entry == null) { continue; } String name = entry.getName(); if (!name.endsWith(".class")) { // only use class files continue; } classes.add(name); } } // now filter classes by changing "/" to "." and trimming the // trailing ".class" Set<String> classNames = new TreeSet<>(); for (String name : classes) { classNames.add(name.replace('/', '.').substring(0, name.length() - 6)); } return classNames; } private static Set<String> getClassFiles( String root, Set<String> holder, File directory, int[] maxDirectories) { // we have passed the maximum number of directories to scan if (maxDirectories[0]-- < 0) { return holder; } File[] files = directory.listFiles(); if (files == null) { return holder; } for (File file : files) { String name = file.getAbsolutePath(); if (!name.startsWith(root)) { continue; } else if (file.isDirectory()) { getClassFiles(root, holder, file, maxDirectories); } else if (file.getName().endsWith(".class")) { holder.add(file.getAbsolutePath().substring(root.length() + 1)); } } return holder; } } // End ClassNameCompleter.java