org.apache.hive.beeline.ClassNameCompleter Maven / Gradle / Ivy
The newest version!
/**
* Copyright (c) 2002-2006, Marc Prud'hommeaux
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with
* the distribution.
*
* Neither the name of JLine nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hive.beeline;
import jline.console.completer.StringsCompleter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.FileInputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.Enumeration;
import java.util.TreeSet;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipEntry;
/**
* the completer is original provided in JLine 0.9.94 and is being removed in 2.12. Add the
* previous implement for usage of the beeline.
*/
public class ClassNameCompleter extends StringsCompleter {
private static final Logger LOG = LoggerFactory.getLogger(ClassNameCompleter.class.getName());
public final static String clazzFileNameExtension = ".class";
public final static String jarFileNameExtension = ".jar";
public ClassNameCompleter(String... candidates) {
super(candidates);
}
public static String[] getClassNames() throws IOException {
Set urls = new HashSet();
for (ClassLoader loader = Thread.currentThread().getContextClassLoader(); loader != null;
loader = loader.getParent()) {
if (!(loader instanceof URLClassLoader)) {
continue;
}
urls.addAll(Arrays.asList(((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 = new Class[]{String.class, javax.swing.JFrame.class};
for (int i = 0; i < systemClasses.length; i++) {
URL classURL = systemClasses[i]
.getResource("/" + systemClasses[i].getName().replace('.', '/') + clazzFileNameExtension);
if (classURL != null) {
URLConnection uc = classURL.openConnection();
if (uc instanceof JarURLConnection) {
urls.add(((JarURLConnection) uc).getJarFileURL());
}
}
}
Set classes = new HashSet();
for (Iterator i = urls.iterator(); i.hasNext(); ) {
URL url = (URL) i.next();
try {
File file = new File(url.getFile());
if (file.isDirectory()) {
Set files = getClassFiles(file.getAbsolutePath(), new HashSet(), file, new int[] { 200 });
classes.addAll(files);
continue;
}
if (!isJarFile(file)) {
continue;
}
JarFile jf = new JarFile(file);
for (Enumeration e = jf.entries(); e.hasMoreElements();) {
JarEntry entry = (JarEntry) e.nextElement();
if (entry == null) {
continue;
}
String name = entry.getName();
if (isClazzFile(name)) {
/* only use class file */
classes.add(name);
} else if (isJarFile(name)) {
classes.addAll(getClassNamesFromJar(name));
} else {
continue;
}
}
} catch (IOException e) {
throw new IOException(String.format("Error reading classpath entry: %s", url), e);
}
}
// now filter classes by changing "/" to "." and trimming the
// trailing ".class"
Set classNames = new TreeSet();
for (Iterator i = classes.iterator(); i.hasNext(); ) {
String name = (String) i.next();
classNames.add(name.replace('/', '.').substring(0, name.length() - 6));
}
return (String[]) classNames.toArray(new String[classNames.size()]);
}
private static Set getClassFiles(String root, Set 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();
for (int i = 0; (files != null) && (i < files.length); i++) {
String name = files[i].getAbsolutePath();
if (!(name.startsWith(root))) {
continue;
} else if (files[i].isDirectory()) {
getClassFiles(root, holder, files[i], maxDirectories);
} else if (files[i].getName().endsWith(clazzFileNameExtension)) {
holder.add(files[i].getAbsolutePath().
substring(root.length() + 1));
}
}
return holder;
}
/**
* Get clazz names from a jar file path
* @param path specifies the jar file's path
* @return
*/
private static List getClassNamesFromJar(String path) {
List classNames = new ArrayList();
ZipInputStream zip = null;
try {
zip = new ZipInputStream(new FileInputStream(path));
ZipEntry entry = zip.getNextEntry();
while (entry != null) {
if (!entry.isDirectory() && entry.getName().endsWith(clazzFileNameExtension)) {
StringBuilder className = new StringBuilder();
for (String part : entry.getName().split("/")) {
if (className.length() != 0) {
className.append(".");
}
className.append(part);
if (part.endsWith(clazzFileNameExtension)) {
className.setLength(className.length() - clazzFileNameExtension.length());
}
}
classNames.add(className.toString());
}
entry = zip.getNextEntry();
}
} catch (IOException e) {
LOG.error("Fail to parse the class name from the Jar file due to the exception:" + e);
} finally {
if (zip != null) {
try {
zip.close();
} catch (IOException e) {
LOG.error("Fail to close the file due to the exception:" + e);
}
}
}
return classNames;
}
private static boolean isJarFile(File file) {
return (file != null && file.isFile() && isJarFile(file.getName()));
}
private static boolean isJarFile(String fileName) {
return fileName.endsWith(jarFileNameExtension);
}
private static boolean isClazzFile(String clazzName) {
return clazzName.endsWith(clazzFileNameExtension);
}
}