blob: 9ce9cbbcc2352e1f5ee71c7ff88a38c035c73bac [file] [log] [blame]
/*
* Copyright 2016 Federico Tomassetti
*
* Licensed 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 com.github.javaparser.symbolsolver.resolution.typesolvers;
import com.github.javaparser.resolution.UnsolvedSymbolException;
import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
import com.github.javaparser.symbolsolver.javassistmodel.JavassistFactory;
import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import java.io.*;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* @author Federico Tomassetti
*/
public class JarTypeSolver implements TypeSolver {
private static JarTypeSolver instance;
private TypeSolver parent;
private Map<String, ClasspathElement> classpathElements = new HashMap<>();
private ClassPool classPool = new ClassPool(false);
public JarTypeSolver(String pathToJar) throws IOException {
addPathToJar(pathToJar);
}
public JarTypeSolver(InputStream jarInputStream) throws IOException {
addPathToJar(jarInputStream);
}
public static JarTypeSolver getJarTypeSolver(String pathToJar) throws IOException {
if (instance == null) {
instance = new JarTypeSolver(pathToJar);
} else {
instance.addPathToJar(pathToJar);
}
return instance;
}
private File dumpToTempFile(InputStream inputStream) throws IOException {
File tempFile = File.createTempFile("jar_file_from_input_stream", ".jar");
tempFile.deleteOnExit();
byte[] buffer = new byte[8 * 1024];
try {
OutputStream output = new FileOutputStream(tempFile);
try {
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
} finally {
output.close();
}
} finally {
inputStream.close();
}
return tempFile;
}
private void addPathToJar(InputStream jarInputStream) throws IOException {
addPathToJar(dumpToTempFile(jarInputStream).getAbsolutePath());
}
private void addPathToJar(String pathToJar) throws IOException {
try {
classPool.appendClassPath(pathToJar);
classPool.appendSystemPath();
} catch (NotFoundException e) {
throw new RuntimeException(e);
}
JarFile jarFile = new JarFile(pathToJar);
JarEntry entry = null;
Enumeration<JarEntry> e = jarFile.entries();
while (e.hasMoreElements()) {
entry = e.nextElement();
if (entry != null && !entry.isDirectory() && entry.getName().endsWith(".class")) {
String name = entryPathToClassName(entry.getName());
classpathElements.put(name, new ClasspathElement(jarFile, entry, name));
}
}
}
@Override
public TypeSolver getParent() {
return parent;
}
@Override
public void setParent(TypeSolver parent) {
this.parent = parent;
}
private String entryPathToClassName(String entryPath) {
if (!entryPath.endsWith(".class")) {
throw new IllegalStateException();
}
String className = entryPath.substring(0, entryPath.length() - ".class".length());
className = className.replace('/', '.');
className = className.replace('$', '.');
return className;
}
@Override
public SymbolReference<ResolvedReferenceTypeDeclaration> tryToSolveType(String name) {
try {
if (classpathElements.containsKey(name)) {
return SymbolReference.solved(
JavassistFactory.toTypeDeclaration(classpathElements.get(name).toCtClass(), getRoot()));
} else {
return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public ResolvedReferenceTypeDeclaration solveType(String name) throws UnsolvedSymbolException {
SymbolReference<ResolvedReferenceTypeDeclaration> ref = tryToSolveType(name);
if (ref.isSolved()) {
return ref.getCorrespondingDeclaration();
} else {
throw new UnsolvedSymbolException(name);
}
}
private class ClasspathElement {
private JarFile jarFile;
private JarEntry entry;
private String path;
ClasspathElement(JarFile jarFile, JarEntry entry, String path) {
this.jarFile = jarFile;
this.entry = entry;
this.path = path;
}
CtClass toCtClass() throws IOException {
try (InputStream is = jarFile.getInputStream(entry)) {
return classPool.makeClass(is);
}
}
}
}