org.kawanfw.file.reflection.ClassFileLocatorNew.java Source code

Java tutorial

Introduction

Here is the source code for org.kawanfw.file.reflection.ClassFileLocatorNew.java

Source

/*
 * This file is part of Awake FILE. 
 * Awake file: Easy file upload & download over HTTP with Java.                                    
 * Copyright (C) 2015,  KawanSoft SAS
 * (http://www.kawansoft.com). All rights reserved.                                
 *                                                                               
 * Awake FILE 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.            
 *                                                                               
 * Awake FILE 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, write to the Free Software           
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
 * 02110-1301  USA
 *
 * Any modifications to this file must keep this entire header
 * intact.
 */
package org.kawanfw.file.reflection;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.logging.Level;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.kawanfw.commons.util.ClientLogger;
import org.kawanfw.commons.util.FrameworkDebug;
import org.kawanfw.commons.util.Tag;

/**
 * Allows to find the ".class" or ".jar" file of an instance, allows to open a InputStream on it.
 * 
 * @author Nicolas de Pomereu
 *
 */
public class ClassFileLocatorNew {

    /** For debug info */
    private static boolean DEBUG = FrameworkDebug.isSet(ClassFileLocatorNew.class);

    /** The class to locate */
    private Class<?> clazz = null;

    /** The name of the class that calls RemoteFile.list(filter) or RemoteFile.listFile(filter) */
    private String callerClassName = null;

    /**
     * Constructor
     * @param clazz   The class to locate
     * @param callerClassName the name of the class that called clazz
     */
    public ClassFileLocatorNew(Class<?> clazz, String callerClassName) {

        if (clazz == null) {
            throw new IllegalArgumentException("class is null!");
        }
        this.clazz = clazz;
        this.callerClassName = callerClassName;

    }

    //    /**
    //     * Exports the class to a temp file "./kawansoft/tmp/className.class"
    //     * <br>Note: className includes package as in Class.getName().
    //     * 
    //     * @return the exported temp file "./kawansoft/tmp/className.class"
    //     * @throws IOException
    //     */
    //    public File exportClassToTempFile() throws IOException {
    //   
    //   String tempDir = FrameworkFileUtil.getKawansoftTempDir();
    //   File outFile = new File(tempDir + File.separator
    //      + clazz.getName() + ".class");
    //   byte [] byteArray = null;
    //   
    //   if (FrameworkSystemUtil.isAndroid()) {
    //       String classname = clazz.getName();
    //       byteArray = AndroidUtil.extractClassFileBytecode(classname);
    //   } else {
    //       byteArray = this.extractClassFileBytecode();
    //   }
    //   
    //   debug("class name      : " + clazz.getName());
    //   debug("byteArray.length: " + byteArray.length);
    //   
    //   FileUtils.writeByteArrayToFile(outFile, byteArray);
    //   return outFile;
    //    }

    /**
     * Gets the container of the class, ie the .jar file or the .class file
     * @return the container of the class, ie the .jar file or the .class file
     * @throws IOException
     */
    public File getContainerFile() throws IOException {

        if (isContainerJar()) {

            //url : jar:file:/I:/_dev_awake/awake-file-3.0/lib/stringtemplate.jar!/org/antlr/stringtemplate/StringTemplate.class
            URL url = getUrl();
            String urlString = url.toString();

            urlString = StringUtils.substringBefore(urlString, "!");
            urlString = StringUtils.substringAfter(urlString, "jar:");

            File file = null;

            try {
                file = new File(new URL(urlString).toURI());
            } catch (URISyntaxException e) {
                throw new IOException(e);
            }
            return file;
        } else {
            return getDotClassFile();
        }
    }

    /**
     * Returns a byte array of the class container (.class or .jar container)
     * @return a byte array of the class container (.class or .jar container)
     * @throws IOException
     */
    public byte[] extractClassFileBytecode() throws IOException {

        InputStream in = null;

        try {
            if (isContainerJar()) {
                in = getUrl().openStream();
            } else {
                File file = getDotClassFile();
                in = new BufferedInputStream(new FileInputStream(file));
            }

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            IOUtils.copy(in, out);
            return out.toByteArray();

        } finally {
            IOUtils.closeQuietly(in);
        }
    }

    /**
     * Returns true if the container is jar file
     * @return true if the container is jar file
     */
    public boolean isContainerJar() {
        URL url = getUrl();

        if (url == null) {
            return false;
        }

        if (url.toString().startsWith("jar:")) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Returns true if the container is .class file
     * @return true if the container is .class file
     */
    public boolean isContainerDotClass() {
        URL url = getUrl();

        if (url == null) {
            return false;
        }

        if (url.toString().startsWith("file:")) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Get the file corresponding to our class stored in .class file
     * @return   file corresponding to our class stored in .class file
     * @throws FileNotFoundException
     * @throws IOException 
     */
    public File getDotClassFile() throws FileNotFoundException, IOException {

        if (!isContainerDotClass()) {
            throw new IllegalArgumentException("class is stored in a jar file. No \".class\" file exists!");
        }

        File file = null;
        try {
            file = new File(getUrl().toURI());
        } catch (URISyntaxException e) {
            throw new IOException(e);
        }

        debug("file: " + file);

        if (file.isFile()) {
            return file;
        }

        debug("dir: " + file);

        if (!file.exists()) {
            throw new FileNotFoundException("Class file does nots exists: " + file);
        }

        // It is a directory ==> Find all classe that matches name

        // Its not a regular class but a private static inner class whose name
        // ends with $
        // Get the first one we find in the directory

        FilenameFilter filenameFilter = new FilenameFilter() {

            @Override
            public boolean accept(File dir, String name) {
                if (name.endsWith("$" + clazz.getSimpleName() + ".class"))
                    return true;
                else
                    return false;
            }
        };

        File[] files = file.listFiles(filenameFilter);
        if (files == null || files.length == 0) {
            throw new FileNotFoundException(
                    Tag.PRODUCT + " Impossible to find the static inner class file for " + clazz.getSimpleName());
        }

        for (File file2 : files) {
            debug("file2 : " + file2);
        }

        // Easy case
        if (files.length == 1) {
            debug("OK: returning " + files[0]);
            return files[0];
        }

        if (callerClassName == null) {
            throw new FileNotFoundException(Tag.PRODUCT + " More than one inner class matching inner class name. "
                    + "Impossible to find the correct static inner class file for " + clazz.getSimpleName()
                    + " without the caller class name.");
        }

        // More that one class and we know the caller
        if (callerClassName.contains(".")) {
            callerClassName = StringUtils.substringAfterLast(callerClassName, ".");
        }

        for (File theFile : files) {
            String filename = theFile.getName();
            if (filename.startsWith(callerClassName)) {
                debug("OK: returning " + theFile);
                return theFile;
            }
        }

        throw new FileNotFoundException(Tag.PRODUCT
                + " More than one inner class matching inner class name. Impossible to find the correct static inner class file for "
                + clazz.getSimpleName());

    }

    /**
     * Returns the URL of the class. If the class is an inner class, will return the directory
     * @return the URL of the class. If the class is an inner class, will return the directory 
     */
    public URL getUrl() {
        URL url = clazz.getResource(clazz.getSimpleName() + ".class");

        // URL null ==> inner class ==> Return the directory location
        if (url == null) {
            url = clazz.getResource("");
        }

        return url;
    }

    /**
     * Returns the pathname with "/" file separator notation corresponding to the package organization.
     * <br>
     * Example : for class org.kawanfw.file.reflection.ClassFileLocator, will
     * return /org/kawanfw/file/reflection/ 
     * @return the pathname corresponding to the package organization
     */
    public static String getClassSubdirectories(String className) {

        if (className == null) {
            throw new IllegalArgumentException("className is null!");
        }

        String directoryPath = className;
        if (!directoryPath.contains(".")) {
            return ""; // No subdirectories. it's a default package class
        }

        directoryPath = StringUtils.substringBeforeLast(directoryPath, ".");
        directoryPath = directoryPath.replace('.', '/');
        directoryPath = "/" + directoryPath + "/";
        return directoryPath;
    }

    /**
     * debug tool
     */
    private void debug(String s) {
        if (DEBUG) {
            ClientLogger.getLogger().log(Level.WARNING, s);
        }
    }

}