/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */
package org.apache.cxf.jca.core.classloader;


import java.io.*;
import java.net.*;
import java.util.*;
import java.util.logging.Logger;

import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.jca.jarloader.JarLoader;


public final class PlugInClassLoaderHelper {
    private static final Logger LOG =
        LogUtils.getL7dLogger(PlugInClassLoaderHelper.class);
    private static Map<String, byte[]> nonClassesMap = new HashMap<>();


    private PlugInClassLoaderHelper() {
        // singleton
    }

    public static boolean hasResource(String name) {
        try {
            return getResourceAsBytes(name) != null;
        } catch (IOException ex) {
            LOG.fine("unexpected exception: " + ex);

            return false;
        }
    }

    public static byte[] getResourceAsBytes(String name) throws IOException {
        // check nonClassCache for properties etc..
        if (!name.endsWith(".class") && nonClassesMap.containsKey(name)) {
            return nonClassesMap.get(name);
        }

        // first check file path directorys, then check jars
        if (!isJarReference(name)) {
            // try direct load of url
            try {
                return JarLoader.getBytesFromInputStream(new URL(name).openStream());
            } catch (java.net.MalformedURLException mue) {
                throw new IOException(mue.getMessage());
            }
        }
        // something with !/
        // check for a nested directory reference
        if (isNestedDirectoryReference(name)) {
            throw new IOException(
                    "Accessing contents of directories within jars is currently not supported");
        }
        String enclosingJar = name.substring(0, name.lastIndexOf("!/") + 2);
        String resourceName = name.substring(name.lastIndexOf("!/") + 2);
        Map<?, ?> jarMap = JarLoader.getJarContents(enclosingJar);

        if (null != jarMap && jarMap.containsKey(resourceName)) {
            byte bytes[] = (byte[])jarMap.get(resourceName);

            // this class will not be looked for again
            // once it is loaded so to save memory we
            // remove it form the map, if it is not a
            // class we add it to the nonClasses cache,
            // this is only true for in memory cache.
            // REVISIT - this needs to be more specific,
            // some classes Home|Remote interfaces are
            // loaded multiple times - see remote class
            // downloading for the moment disable this
            // jarMap.remove(resourceName);
            //
            if (!name.endsWith(".class")) {
                nonClassesMap.put(name, bytes);
            }
            return bytes;
        }

        // failed to locate the resource
        return null;
    }

    private static boolean isJarReference(String name) {
        return name.indexOf("!/") != -1;
    }

    private static boolean isNestedDirectoryReference(String path) {
        String nestedDir = path.substring(path.lastIndexOf("!/") + 2);

        return !"".equals(nestedDir) && nestedDir.endsWith("/");
    }
}
