//
// Copyright (C) 2013 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.jvm;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import gov.nasa.jpf.util.FileUtils;
import gov.nasa.jpf.vm.ClassFileMatch;
import gov.nasa.jpf.vm.ClassParseException;
/**
* a ClassFileContainer that loads classes from jar files
*/
public class JarClassFileContainer extends JVMClassFileContainer {
protected JarFile jar;
protected String pathPrefix; // optional
static String getContainerUrl (File file){
try {
return "jar:" + file.toURI().toURL().toString() + "!/";
} catch (MalformedURLException x) {
return "jar:" + file.getAbsolutePath() + "!/";
}
}
public JarClassFileContainer (File file) throws IOException {
super(file.getPath(), getContainerUrl(file));
jar = new JarFile(file);
}
public JarClassFileContainer (File file, String pathPrefix) throws IOException {
super(getPath(file, pathPrefix), getContainerUrl(file));
jar = new JarFile(file);
this.pathPrefix = getNormalizedPathPrefix(pathPrefix);
}
/**
* make sure the return value ends with '/', and does NOT start with '/'. If
* the supplied pathPrefix only contains '/' or an empty string, return null
*/
static String getNormalizedPathPrefix(String pathPrefix){
if (pathPrefix != null){
int len = pathPrefix.length();
if (len > 0){
if (pathPrefix.charAt(0) == '/'){
if (len == 1){
return null; // no need for storing a single '/' prefix
} else {
pathPrefix = pathPrefix.substring(1); // skip the heading '/'
len--;
}
}
if (pathPrefix.charAt(len-1) != '/'){
pathPrefix += '/';
}
return pathPrefix;
} else {
return null; // empty prefix
}
} else {
return null; // null prefix
}
}
/**
* return our string representation of the complete spec, which is
*
* <jar-pathname>/pathPrefix
*/
static String getPath(File file, String pathPrefix){
String pn = file.getPath();
if (pathPrefix != null){
int len = pathPrefix.length();
if (len > 0){
if (pathPrefix.charAt(0) == '/'){
if (len == 1){
return pn; // no need to store a single '/'
}
} else {
pn += '/';
}
pn += pathPrefix;
}
}
return pn;
}
@Override
public ClassFileMatch getMatch(String clsName) throws ClassParseException {
String pn = clsName.replace('.', '/') + ".class";
if (pathPrefix != null){
pn = pathPrefix + pn;
}
JarEntry e = jar.getJarEntry(pn);
if (e != null) {
InputStream is = null;
try {
long len = e.getSize();
if (len > Integer.MAX_VALUE) {
error("classfile too big: " + e.getName());
}
is = jar.getInputStream(e);
byte[] data = new byte[(int) len];
FileUtils.getContents(is, data);
return new JVMClassFileMatch(clsName, getClassURL(clsName), data);
} catch (IOException iox) {
error("error reading jar entry " + e.getName());
} finally {
if (is != null) {
try {
is.close();
} catch (IOException iox) {
error("cannot close input stream for file " + e.getName());
}
}
}
}
return null;
}
}