/**
* Copyright (c) 2015, Lucee Assosication Switzerland. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
package lucee.runtime.osgi;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import lucee.commons.io.IOUtil;
import lucee.commons.io.res.Resource;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.Remapper;
import org.objectweb.asm.commons.RemappingClassAdapter;
public class JarUtil {
public static final String[] DEFAULT_IGNORES=new String[]{
"java.*"
};
/**
*
* @param res
* @param ignores ".*" add the end includes all sub directories
* @return
* @throws IOException
*/
public static Set<String> getExternalImports(Resource res,String[] ignores) throws IOException {
InputStream is = res.getInputStream();
try {
return getExternalImports(is,ignores);
}
finally{
IOUtil.closeEL(is);
}
}
public static Set<String> getExternalImports(InputStream is,String[] ignores) {
Set<String> imports=new HashSet<>();
Set<String> classNames=new HashSet<>();
ZipInputStream zis = null;
try{
zis = new ZipInputStream(is);
ZipEntry entry;
String name;
while((entry=zis.getNextEntry())!=null){
if(entry.isDirectory() || !entry.getName().endsWith(".class")) continue;
name=entry.getName();
name=name.replace('/', '.');
name=name.substring(0,name.length()-6);
classNames.add(name);
_getExternalImports(imports,zis,ignores);
}
}
catch(IOException ioe) {
ioe.printStackTrace();
}
finally {
IOUtil.closeEL(zis);
}
// remove all class from this jar
Iterator<String> it = classNames.iterator();
String cn;
while(it.hasNext()){
cn=it.next();
imports.remove(cn);
}
// create package set
Set<String> importPackages=new HashSet<>();
it = imports.iterator();
int index;
while(it.hasNext()){
cn=it.next();
index=cn.lastIndexOf('.');
if(index==-1) continue; // no package
importPackages.add(cn.substring(0,index));
}
return importPackages;
}
private static void _getExternalImports(Set<String> imports, InputStream src,String[] ignores) throws IOException{
final ClassReader reader = new ClassReader(src);
final Remapper remapper = new Collector(imports,ignores);
final ClassVisitor inner = new EmptyVisitor();
final RemappingClassAdapter visitor =
new RemappingClassAdapter(inner, remapper);
reader.accept(visitor, 0);
}
public static class Collector extends Remapper{
private final Set<String> imports;
private final String[] ignores;
public Collector(final Set<String> imports,String[] ignores){
this.imports = imports;
this.ignores = ignores;
}
@Override
public String mapDesc(final String desc){
if(desc.startsWith("L")){
this.addType(desc.substring(1, desc.length() - 1));
}
return super.mapDesc(desc);
}
@Override
public String[] mapTypes(final String[] types){
for(final String type : types){
this.addType(type);
}
return super.mapTypes(types);
}
@Override
public String mapType(final String type){
this.addType(type);
return type;
}
private void addType(final String type){
String className = type.replace('/', '.');
int index=className.lastIndexOf('.');
if(index==-1) return;// class with no package
String ignore,pack;
for(int i=0;i<DEFAULT_IGNORES.length;i++){
ignore=DEFAULT_IGNORES[i];
// also ignore sub directories
if(ignore.endsWith(".*")) {
ignore=ignore.substring(0,ignore.length()-1);
if(className.startsWith(ignore)) return;
}
else {
pack=className.substring(0,index);
if(pack.equals(ignore)) return;
}
}
for(int i=0;i<ignores.length;i++){
ignore=ignores[i];
// also ignore sub directories
if(ignore.endsWith(".*")) {
ignore=ignore.substring(0,ignore.length()-1);
if(className.startsWith(ignore)) return;
}
else {
pack=className.substring(0,index);
if(pack.equals(ignore)) return;
}
}
this.imports.add(className);
}
}
private static class EmptyVisitor extends ClassVisitor implements Opcodes {
public EmptyVisitor() {
super(ASM4);
}
}
}