1 /* 2 * Copyright 2016 Federico Tomassetti 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15 package com.github.javaparser.symbolsolver.resolution.typesolvers; 16 17 import com.github.javaparser.resolution.UnsolvedSymbolException; 18 import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration; 19 import com.github.javaparser.symbolsolver.javassistmodel.JavassistFactory; 20 import com.github.javaparser.symbolsolver.model.resolution.SymbolReference; 21 import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; 22 import javassist.ClassPool; 23 import javassist.CtClass; 24 import javassist.NotFoundException; 25 26 import java.io.*; 27 import java.util.Enumeration; 28 import java.util.HashMap; 29 import java.util.Map; 30 import java.util.jar.JarEntry; 31 import java.util.jar.JarFile; 32 33 /** 34 * @author Federico Tomassetti 35 */ 36 public class JarTypeSolver implements TypeSolver { 37 38 private static JarTypeSolver instance; 39 40 private TypeSolver parent; 41 private Map<String, ClasspathElement> classpathElements = new HashMap<>(); 42 private ClassPool classPool = new ClassPool(false); 43 JarTypeSolver(String pathToJar)44 public JarTypeSolver(String pathToJar) throws IOException { 45 addPathToJar(pathToJar); 46 } 47 JarTypeSolver(InputStream jarInputStream)48 public JarTypeSolver(InputStream jarInputStream) throws IOException { 49 addPathToJar(jarInputStream); 50 } 51 getJarTypeSolver(String pathToJar)52 public static JarTypeSolver getJarTypeSolver(String pathToJar) throws IOException { 53 if (instance == null) { 54 instance = new JarTypeSolver(pathToJar); 55 } else { 56 instance.addPathToJar(pathToJar); 57 } 58 return instance; 59 } 60 dumpToTempFile(InputStream inputStream)61 private File dumpToTempFile(InputStream inputStream) throws IOException { 62 File tempFile = File.createTempFile("jar_file_from_input_stream", ".jar"); 63 tempFile.deleteOnExit(); 64 65 byte[] buffer = new byte[8 * 1024]; 66 67 try { 68 OutputStream output = new FileOutputStream(tempFile); 69 try { 70 int bytesRead; 71 while ((bytesRead = inputStream.read(buffer)) != -1) { 72 output.write(buffer, 0, bytesRead); 73 } 74 } finally { 75 output.close(); 76 } 77 } finally { 78 inputStream.close(); 79 } 80 return tempFile; 81 } 82 addPathToJar(InputStream jarInputStream)83 private void addPathToJar(InputStream jarInputStream) throws IOException { 84 addPathToJar(dumpToTempFile(jarInputStream).getAbsolutePath()); 85 } 86 addPathToJar(String pathToJar)87 private void addPathToJar(String pathToJar) throws IOException { 88 try { 89 classPool.appendClassPath(pathToJar); 90 classPool.appendSystemPath(); 91 } catch (NotFoundException e) { 92 throw new RuntimeException(e); 93 } 94 JarFile jarFile = new JarFile(pathToJar); 95 JarEntry entry = null; 96 Enumeration<JarEntry> e = jarFile.entries(); 97 while (e.hasMoreElements()) { 98 entry = e.nextElement(); 99 if (entry != null && !entry.isDirectory() && entry.getName().endsWith(".class")) { 100 String name = entryPathToClassName(entry.getName()); 101 classpathElements.put(name, new ClasspathElement(jarFile, entry, name)); 102 } 103 } 104 } 105 106 @Override getParent()107 public TypeSolver getParent() { 108 return parent; 109 } 110 111 @Override setParent(TypeSolver parent)112 public void setParent(TypeSolver parent) { 113 this.parent = parent; 114 } 115 entryPathToClassName(String entryPath)116 private String entryPathToClassName(String entryPath) { 117 if (!entryPath.endsWith(".class")) { 118 throw new IllegalStateException(); 119 } 120 String className = entryPath.substring(0, entryPath.length() - ".class".length()); 121 className = className.replace('/', '.'); 122 className = className.replace('$', '.'); 123 return className; 124 } 125 126 @Override tryToSolveType(String name)127 public SymbolReference<ResolvedReferenceTypeDeclaration> tryToSolveType(String name) { 128 try { 129 if (classpathElements.containsKey(name)) { 130 return SymbolReference.solved( 131 JavassistFactory.toTypeDeclaration(classpathElements.get(name).toCtClass(), getRoot())); 132 } else { 133 return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class); 134 } 135 } catch (IOException e) { 136 throw new RuntimeException(e); 137 } 138 } 139 140 @Override solveType(String name)141 public ResolvedReferenceTypeDeclaration solveType(String name) throws UnsolvedSymbolException { 142 SymbolReference<ResolvedReferenceTypeDeclaration> ref = tryToSolveType(name); 143 if (ref.isSolved()) { 144 return ref.getCorrespondingDeclaration(); 145 } else { 146 throw new UnsolvedSymbolException(name); 147 } 148 } 149 150 private class ClasspathElement { 151 private JarFile jarFile; 152 private JarEntry entry; 153 private String path; 154 ClasspathElement(JarFile jarFile, JarEntry entry, String path)155 ClasspathElement(JarFile jarFile, JarEntry entry, String path) { 156 this.jarFile = jarFile; 157 this.entry = entry; 158 this.path = path; 159 } 160 toCtClass()161 CtClass toCtClass() throws IOException { 162 try (InputStream is = jarFile.getInputStream(entry)) { 163 return classPool.makeClass(is); 164 } 165 } 166 } 167 } 168