<commit_before>/*--------------------------------------------------------------------------
 *  Copyright 2011 <NAME>
 *
 *  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.
 *--------------------------------------------------------------------------*/
//--------------------------------------
// snappy-java Project
//
// SnappyLoader.java
// Since: 2011/03/29
//
// $URL$ 
// $Author$
//--------------------------------------
package org.xerial.snappy;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * This class loads a native library of snappy-java (snappyjava.dll,
 * libsnappy.so, etc.) according to the user platform (<i>os.name</i> and
 * <i>os.arch</i>). The natively compiled libraries bundled to snappy-java
 * contain the codes of the original snappy and JNI programs to access Snappy.
 * 
 * In default, no configuration is required to use snappy-java, but you can load
 * your own native library created by 'make native' command.
 * 
 * LoadSnappy searches for native libraries (snappyjava.dll, libsnappy.so, etc.)
 * in the following order:
 * <ol>
 * <li>(System property: <i>org.xerial.snappy.lib.path</i>)/(System property:
 * <i>org.xerial.lib.name</i>)
 * <li>One of the libraries embedded in snappy-java-(version).jar extracted into
 * (System property: <i>java.io.tempdir</i> or if
 * <i>org.xerial.snappy.tempdir</i> is set, use this folder.)
 * <li>Folders specified by java.lib.path system property (This is the default
 * path that JVM searches for native libraries)
 * </ol>
 * 
 * <p>
 * If you do not want to use folder <i>java.io.tempdir</i>, set the System
 * property <i>org.xerial.snappy.tempdir</i>. For example, to use
 * <i>/tmp/leo</i> as a temporary folder to copy native libraries, use -D option
 * of JVM:
 * 
 * <pre>
 * <code>
 * java -Dorg.xerial.snappy.tempdir="/tmp/leo" ...
 * </code>
 * </pre>
 * 
 * </p>
 * 
 * @author leo
 * 
 */
public class SnappyLoader
{
    private static boolean isInitialized = false;
    private static boolean isLoaded      = false;

    private static byte[] getByteCode(String resourcePath) throws IOException {

        InputStream in = SnappyLoader.class.getResourceAsStream(resourcePath);
        if (in == null)
            throw new IOException(resourcePath + " is not found");
        byte[] buf = new byte[1024];
        ByteArrayOutputStream byteCodeBuf = new ByteArrayOutputStream();
        for (int readLength; (readLength = in.read(buf)) != -1;) {
            byteCodeBuf.write(buf, 0, readLength);
        }
        in.close();

        return byteCodeBuf.toByteArray();
    }

    public static boolean isNativeLibraryLoaded() {
        return isLoaded;
    }

    static void load() {

        if (isInitialized)
            return;

        isInitialized = true;
        final String nativeLoaderClassName = "org.xerial.snappy.SnappyNativeLoader";
        final String[] classesToPreload = new String[] { "org.xerial.snappy.SnappyNative",
                "org.xerial.snappy.SnappyErrorCode" };

        try {
            Class.forName(nativeLoaderClassName);
            // If this native loader class is already defined, it means that another class loader already loaded the native library of snappy
            isLoaded = true;
            return;
        }
        catch (ClassNotFoundException e) {
            try {
                // Load a byte code 
                byte[] byteCode = getByteCode("/org/xerial/snappy/SnappyNativeLoader.bytecode");
                // In addition, we need to load the other dependent classes (e.g., SnappyNative and SnappyException) using the system class loader
                List<byte[]> preloadClassByteCode = new ArrayList<byte[]>(classesToPreload.length);
                for (String each : classesToPreload) {
                    preloadClassByteCode.add(getByteCode(String.format("/%s.class", each.replaceAll("\\.", "/"))));
                }

                // Create a new class from a byte code
                Class< ? > classLoader = Class.forName("java.lang.ClassLoader");
                Method defineClass = classLoader.getDeclaredMethod("defineClass", new Class[] { String.class,
                        byte[].class, int.class, int.class, ProtectionDomain.class });

                ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
                // ClassLoader.defineClass is a protected method, so we have to make it accessible
                defineClass.setAccessible(true);
                try {
                    // Create a new class using a ClassLoader#defineClass
                    defineClass.invoke(systemClassLoader, nativeLoaderClassName, byteCode, 0, byteCode.length,
                            System.class.getProtectionDomain());

                    for (int i = 0; i < classesToPreload.length; ++i) {
                        byte[] b = preloadClassByteCode.get(i);
                        defineClass.invoke(systemClassLoader, classesToPreload[i], b, 0, b.length,
                                System.class.getProtectionDomain());
                    }
                }
                finally {
                    // Reset the accessibility to defineClass method
                    defineClass.setAccessible(false);
                }

                // Load the snappy loader class
                Class< ? > loaderClass = systemClassLoader.loadClass(nativeLoaderClassName);
                if (loaderClass != null) {

                    File nativeLib = findNativeLibrary();
                    if (nativeLib != null) {
                        // Load extracted or specified snappyjava native library. 
                        Method loadMethod = loaderClass.getDeclaredMethod("load", new Class[] { String.class });
                        loadMethod.invoke(null, nativeLib.getAbsolutePath());
                    }
                    else {
                        // Load preinstalled snappyjava (in the path -Djava.library.path) 
                        Method loadMethod = loaderClass.getDeclaredMethod("loadLibrary", new Class[] { String.class });
                        loadMethod.invoke(null, "snappyjava");
                    }

                    // And also, preload the other dependent classes 
                    for (String each : classesToPreload) {
                        systemClassLoader.loadClass(each);
                    }
                    isLoaded = true;
                }
            }
            catch (Exception e2) {
                throw new SnappyError(SnappyErrorCode.FAILED_TO_LOAD_NATIVE_LIBRARY, e.getMessage());
            }
        }

    }

    public static final String KEY_SNAPPY_LIB_PATH             = "org.xerial.snappy.lib.path";
    public static final String KEY_SNAPPY_LIB_NAME             = "org.xerial.snappy.lib.name";
    public static final String KEY_SNAPPY_TEMPDIR              = "org.xerial.snappy.tempdir";
    public static final String KEY_SNAPPY_DISABLE_BUNDLED_LIBS = "org.xerial.snappy.disable.bundled.libs";

    /**
     * Computes the MD5 value of the input stream
     * 
     * @param input
     * @return
     * @throws IOException
     * @throws NoSuchAlgorithmException
     */
    static String md5sum(InputStream input) throws IOException {
        BufferedInputStream in = new BufferedInputStream(input);
        try {
            MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
            DigestInputStream digestInputStream = new DigestInputStream(in, digest);
            for (; digestInputStream.read() >= 0;) {

            }
            ByteArrayOutputStream md5out = new ByteArrayOutputStream();
            md5out.write(digest.digest());
            return md5out.toString();
        }
        catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("MD5 algorithm is not available: " + e);
        }
        finally {
            in.close();
        }
    }

    /**
     * Extract the specified library file to the target folder
     * 
     * @param libFolderForCurrentOS
     * @param libraryFileName
     * @param targetFolder
     * @return
     */
    private static File extractLibraryFile(String libFolderForCurrentOS, String libraryFileName, String targetFolder) {
        String nativeLibraryFilePath = libFolderForCurrentOS + "/" + libraryFileName;
        final String prefix = "snappy-" + getVersion() + "-";
        String extractedLibFileName = prefix + libraryFileName;
        File extractedLibFile = new File(targetFolder, extractedLibFileName);

        try {
            if (extractedLibFile.exists()) {
                // test md5sum value
                String md5sum1 = md5sum(SnappyLoader.class.getResourceAsStream(nativeLibraryFilePath));
                String md5sum2 = md5sum(new FileInputStream(extractedLibFile));

                if (md5sum1.equals(md5sum2)) {
                    return new File(targetFolder, extractedLibFileName);
                }
                else {
                    // remove old native library file
                    boolean deletionSucceeded = extractedLibFile.delete();
                    if (!deletionSucceeded) {
                        throw new IOException("failed to remove existing native library file: "
                                + extractedLibFile.getAbsolutePath());
                    }
                }
            }

            // Extract a native library file into the target directory
            InputStream reader = SnappyLoader.class.getResourceAsStream(nativeLibraryFilePath);
            FileOutputStream writer = new FileOutputStream(extractedLibFile);
            byte[] buffer = new byte[8192];
            int bytesRead = 0;
            while ((bytesRead = reader.read(buffer)) != -1) {
                writer.write(buffer, 0, bytesRead);
            }

            writer.close();
            reader.close();

            // Set executable (x) flag to enable Java to load the native library
            if (!System.getProperty("os.name").contains("Windows")) {
                try {
                    Runtime.getRuntime().exec(new String[] { "chmod", "755", extractedLibFile.getAbsolutePath() })
                            .waitFor();
                }
                catch (Throwable e) {}
            }

            return new File(targetFolder, extractedLibFileName);
        }
        catch (IOException e) {
            e.printStackTrace(System.err);
            return null;
        }
    }

    static File findNativeLibrary() {
        // Try to load the library from org.xerial.snappy.lib.path library path */
        String snappyNativeLibraryPath = System.getProperty(KEY_SNAPPY_LIB_PATH);
        String snappyNativeLibraryName = System.getProperty(KEY_SNAPPY_LIB_NAME);

        if (System.getProperty(KEY_SNAPPY_DISABLE_BUNDLED_LIBS, "false").equals("false")) {
            // Resolve the library file name with a suffix (e.g., dll, .so, etc.) 
            if (snappyNativeLibraryName == null)
                snappyNativeLibraryName = System.mapLibraryName("snappyjava");

            if (snappyNativeLibraryPath != null) {
                File nativeLib = new File(snappyNativeLibraryPath, snappyNativeLibraryName);
                if (nativeLib.exists())
                    return nativeLib;
            }
        }

        {
            // Load an OS-dependent native library inside a jar file
            snappyNativeLibraryPath = "/org/xerial/snappy/native/" + OSInfo.getNativeLibFolderPathForCurrentOS();

            if (SnappyLoader.class.getResource(snappyNativeLibraryPath + "/" + snappyNativeLibraryName) != null) {
                // Temporary library folder. Use the value of org.xerial.snappy.tempdir or java.io.tmpdir
                String tempFolder = new File(System.getProperty(KEY_SNAPPY_TEMPDIR,
                        System.getProperty("java.io.tmpdir"))).getAbsolutePath();

                // Extract and load a native library inside the jar file
                return extractLibraryFile(snappyNativeLibraryPath, snappyNativeLibraryName, tempFolder);
            }
        }

        return null;
    }

    private static void getNativeLibraryFolderForTheCurrentOS() {
        String osName = OSInfo.getOSName();
        String archName = OSInfo.getArchName();

    }

    public static int getMajorVersion() {
        String[] c = getVersion().split("\\.");
        return (c.length > 0) ? Integer.parseInt(c[0]) : 1;
    }

    public static int getMinorVersion() {
        String[] c = getVersion().split("\\.");
        return (c.length > 1) ? Integer.parseInt(c[1]) : 0;
    }

    public static String getVersion() {

        URL versionFile = SnappyLoader.class
                .getResource("/META-INF/maven/org.xerial.snappy/snappy-java/pom.properties");
        if (versionFile == null)
            versionFile = SnappyLoader.class.getResource("/org/xerial/snappy/VERSION");

        String version = "unknown";
        try {
            if (versionFile != null) {
                Properties versionData = new Properties();
                versionData.load(versionFile.openStream());
                version = versionData.getProperty("version", version);
                if (version.equals("unknown"))
                    version = versionData.getProperty("VERSION", version);
                version = version.trim().replaceAll("[^0-9\\.]", "");
            }
        }
        catch (IOException e) {
            System.err.println(e);
        }
        return version;
    }

}
<commit_msg>Traverse ancestor class loader<commit_after>/*--------------------------------------------------------------------------
 *  Copyright 2011 <NAME>
 *
 *  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.
 *--------------------------------------------------------------------------*/
//--------------------------------------
// snappy-java Project
//
// SnappyLoader.java
// Since: 2011/03/29
//
// $URL$ 
// $Author$
//--------------------------------------
package org.xerial.snappy;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * This class loads a native library of snappy-java (snappyjava.dll,
 * libsnappy.so, etc.) according to the user platform (<i>os.name</i> and
 * <i>os.arch</i>). The natively compiled libraries bundled to snappy-java
 * contain the codes of the original snappy and JNI programs to access Snappy.
 * 
 * In default, no configuration is required to use snappy-java, but you can load
 * your own native library created by 'make native' command.
 * 
 * LoadSnappy searches for native libraries (snappyjava.dll, libsnappy.so, etc.)
 * in the following order:
 * <ol>
 * <li>(System property: <i>org.xerial.snappy.lib.path</i>)/(System property:
 * <i>org.xerial.lib.name</i>)
 * <li>One of the libraries embedded in snappy-java-(version).jar extracted into
 * (System property: <i>java.io.tempdir</i> or if
 * <i>org.xerial.snappy.tempdir</i> is set, use this folder.)
 * <li>Folders specified by java.lib.path system property (This is the default
 * path that JVM searches for native libraries)
 * </ol>
 * 
 * <p>
 * If you do not want to use folder <i>java.io.tempdir</i>, set the System
 * property <i>org.xerial.snappy.tempdir</i>. For example, to use
 * <i>/tmp/leo</i> as a temporary folder to copy native libraries, use -D option
 * of JVM:
 * 
 * <pre>
 * <code>
 * java -Dorg.xerial.snappy.tempdir="/tmp/leo" ...
 * </code>
 * </pre>
 * 
 * </p>
 * 
 * @author leo
 * 
 */
public class SnappyLoader
{
    private static boolean isInitialized = false;
    private static boolean isLoaded      = false;

    private static ClassLoader getAncestorClassLoader() {
        ClassLoader cl = SnappyLoader.class.getClassLoader();
        while (cl.getParent() != null) {
            cl = cl.getParent();
        }
        return cl;
    }

    private static byte[] getByteCode(String resourcePath) throws IOException {

        InputStream in = SnappyLoader.class.getResourceAsStream(resourcePath);
        if (in == null)
            throw new IOException(resourcePath + " is not found");
        byte[] buf = new byte[1024];
        ByteArrayOutputStream byteCodeBuf = new ByteArrayOutputStream();
        for (int readLength; (readLength = in.read(buf)) != -1;) {
            byteCodeBuf.write(buf, 0, readLength);
        }
        in.close();

        return byteCodeBuf.toByteArray();
    }

    public static boolean isNativeLibraryLoaded() {
        return isLoaded;
    }

    static void load() {

        if (isInitialized)
            return;

        isInitialized = true;
        final String nativeLoaderClassName = "org.xerial.snappy.SnappyNativeLoader";
        final String[] classesToPreload = new String[] { "org.xerial.snappy.SnappyNative",
                "org.xerial.snappy.SnappyErrorCode" };

        try {
            Class.forName(nativeLoaderClassName);
            // If this native loader class is already defined, it means that another class loader already loaded the native library of snappy
            isLoaded = true;
            return;
        }
        catch (ClassNotFoundException e) {
            try {
                // Load a byte code 
                byte[] byteCode = getByteCode("/org/xerial/snappy/SnappyNativeLoader.bytecode");
                // In addition, we need to load the other dependent classes (e.g., SnappyNative and SnappyException) using the system class loader
                List<byte[]> preloadClassByteCode = new ArrayList<byte[]>(classesToPreload.length);
                for (String each : classesToPreload) {
                    preloadClassByteCode.add(getByteCode(String.format("/%s.class", each.replaceAll("\\.", "/"))));
                }

                // Create a new class from a byte code
                Class< ? > classLoader = Class.forName("java.lang.ClassLoader");
                Method defineClass = classLoader.getDeclaredMethod("defineClass", new Class[] { String.class,
                        byte[].class, int.class, int.class, ProtectionDomain.class });

                ClassLoader systemClassLoader = getAncestorClassLoader();
                // ClassLoader.defineClass is a protected method, so we have to make it accessible
                defineClass.setAccessible(true);
                try {
                    // Create a new class using a ClassLoader#defineClass
                    defineClass.invoke(systemClassLoader, nativeLoaderClassName, byteCode, 0, byteCode.length,
                            System.class.getProtectionDomain());

                    for (int i = 0; i < classesToPreload.length; ++i) {
                        byte[] b = preloadClassByteCode.get(i);
                        defineClass.invoke(systemClassLoader, classesToPreload[i], b, 0, b.length,
                                System.class.getProtectionDomain());
                    }
                }
                finally {
                    // Reset the accessibility to defineClass method
                    defineClass.setAccessible(false);
                }

                // Load the snappy loader class
                Class< ? > loaderClass = systemClassLoader.loadClass(nativeLoaderClassName);
                if (loaderClass != null) {

                    File nativeLib = findNativeLibrary();
                    if (nativeLib != null) {
                        // Load extracted or specified snappyjava native library. 
                        Method loadMethod = loaderClass.getDeclaredMethod("load", new Class[] { String.class });
                        loadMethod.invoke(null, nativeLib.getAbsolutePath());
                    }
                    else {
                        // Load preinstalled snappyjava (in the path -Djava.library.path) 
                        Method loadMethod = loaderClass.getDeclaredMethod("loadLibrary", new Class[] { String.class });
                        loadMethod.invoke(null, "snappyjava");
                    }

                    // And also, preload the other dependent classes 
                    for (String each : classesToPreload) {
                        systemClassLoader.loadClass(each);
                    }
                    isLoaded = true;
                }
            }
            catch (Exception e2) {
                throw new SnappyError(SnappyErrorCode.FAILED_TO_LOAD_NATIVE_LIBRARY, e.getMessage());
            }
        }

    }

    public static final String KEY_SNAPPY_LIB_PATH             = "org.xerial.snappy.lib.path";
    public static final String KEY_SNAPPY_LIB_NAME             = "org.xerial.snappy.lib.name";
    public static final String KEY_SNAPPY_TEMPDIR              = "org.xerial.snappy.tempdir";
    public static final String KEY_SNAPPY_DISABLE_BUNDLED_LIBS = "org.xerial.snappy.disable.bundled.libs";

    /**
     * Computes the MD5 value of the input stream
     * 
     * @param input
     * @return
     * @throws IOException
     * @throws NoSuchAlgorithmException
     */
    static String md5sum(InputStream input) throws IOException {
        BufferedInputStream in = new BufferedInputStream(input);
        try {
            MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
            DigestInputStream digestInputStream = new DigestInputStream(in, digest);
            for (; digestInputStream.read() >= 0;) {

            }
            ByteArrayOutputStream md5out = new ByteArrayOutputStream();
            md5out.write(digest.digest());
            return md5out.toString();
        }
        catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("MD5 algorithm is not available: " + e);
        }
        finally {
            in.close();
        }
    }

    /**
     * Extract the specified library file to the target folder
     * 
     * @param libFolderForCurrentOS
     * @param libraryFileName
     * @param targetFolder
     * @return
     */
    private static File extractLibraryFile(String libFolderForCurrentOS, String libraryFileName, String targetFolder) {
        String nativeLibraryFilePath = libFolderForCurrentOS + "/" + libraryFileName;
        final String prefix = "snappy-" + getVersion() + "-";
        String extractedLibFileName = prefix + libraryFileName;
        File extractedLibFile = new File(targetFolder, extractedLibFileName);

        try {
            if (extractedLibFile.exists()) {
                // test md5sum value
                String md5sum1 = md5sum(SnappyLoader.class.getResourceAsStream(nativeLibraryFilePath));
                String md5sum2 = md5sum(new FileInputStream(extractedLibFile));

                if (md5sum1.equals(md5sum2)) {
                    return new File(targetFolder, extractedLibFileName);
                }
                else {
                    // remove old native library file
                    boolean deletionSucceeded = extractedLibFile.delete();
                    if (!deletionSucceeded) {
                        throw new IOException("failed to remove existing native library file: "
                                + extractedLibFile.getAbsolutePath());
                    }
                }
            }

            // Extract a native library file into the target directory
            InputStream reader = SnappyLoader.class.getResourceAsStream(nativeLibraryFilePath);
            FileOutputStream writer = new FileOutputStream(extractedLibFile);
            byte[] buffer = new byte[8192];
            int bytesRead = 0;
            while ((bytesRead = reader.read(buffer)) != -1) {
                writer.write(buffer, 0, bytesRead);
            }

            writer.close();
            reader.close();

            // Set executable (x) flag to enable Java to load the native library
            if (!System.getProperty("os.name").contains("Windows")) {
                try {
                    Runtime.getRuntime().exec(new String[] { "chmod", "755", extractedLibFile.getAbsolutePath() })
                            .waitFor();
                }
                catch (Throwable e) {}
            }

            return new File(targetFolder, extractedLibFileName);
        }
        catch (IOException e) {
            e.printStackTrace(System.err);
            return null;
        }
    }

    static File findNativeLibrary() {
        // Try to load the library from org.xerial.snappy.lib.path library path */
        String snappyNativeLibraryPath = System.getProperty(KEY_SNAPPY_LIB_PATH);
        String snappyNativeLibraryName = System.getProperty(KEY_SNAPPY_LIB_NAME);

        if (System.getProperty(KEY_SNAPPY_DISABLE_BUNDLED_LIBS, "false").equals("false")) {
            // Resolve the library file name with a suffix (e.g., dll, .so, etc.) 
            if (snappyNativeLibraryName == null)
                snappyNativeLibraryName = System.mapLibraryName("snappyjava");

            if (snappyNativeLibraryPath != null) {
                File nativeLib = new File(snappyNativeLibraryPath, snappyNativeLibraryName);
                if (nativeLib.exists())
                    return nativeLib;
            }
        }

        {
            // Load an OS-dependent native library inside a jar file
            snappyNativeLibraryPath = "/org/xerial/snappy/native/" + OSInfo.getNativeLibFolderPathForCurrentOS();

            if (SnappyLoader.class.getResource(snappyNativeLibraryPath + "/" + snappyNativeLibraryName) != null) {
                // Temporary library folder. Use the value of org.xerial.snappy.tempdir or java.io.tmpdir
                String tempFolder = new File(System.getProperty(KEY_SNAPPY_TEMPDIR,
                        System.getProperty("java.io.tmpdir"))).getAbsolutePath();

                // Extract and load a native library inside the jar file
                return extractLibraryFile(snappyNativeLibraryPath, snappyNativeLibraryName, tempFolder);
            }
        }

        return null;
    }

    private static void getNativeLibraryFolderForTheCurrentOS() {
        String osName = OSInfo.getOSName();
        String archName = OSInfo.getArchName();

    }

    public static int getMajorVersion() {
        String[] c = getVersion().split("\\.");
        return (c.length > 0) ? Integer.parseInt(c[0]) : 1;
    }

    public static int getMinorVersion() {
        String[] c = getVersion().split("\\.");
        return (c.length > 1) ? Integer.parseInt(c[1]) : 0;
    }

    public static String getVersion() {

        URL versionFile = SnappyLoader.class
                .getResource("/META-INF/maven/org.xerial.snappy/snappy-java/pom.properties");
        if (versionFile == null)
            versionFile = SnappyLoader.class.getResource("/org/xerial/snappy/VERSION");

        String version = "unknown";
        try {
            if (versionFile != null) {
                Properties versionData = new Properties();
                versionData.load(versionFile.openStream());
                version = versionData.getProperty("version", version);
                if (version.equals("unknown"))
                    version = versionData.getProperty("VERSION", version);
                version = version.trim().replaceAll("[^0-9\\.]", "");
            }
        }
        catch (IOException e) {
            System.err.println(e);
        }
        return version;
    }

}

{'fraction_non_alphanumeric': 0.11195998492582822, 'fraction_numerical': 0.004316694645243071, 'mean_word_length': 1.9734134664357748, 'pattern_counts': {'":': 0, '<': 73, '<?xml version=': 0, '>': 77, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2488600', 'n_tokens_mistral': 7618, 'n_tokens_neox': 7298, 'n_words': 1959}