/*
* Copyright (c) 2007-2009, Osmorc Development Team
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
* * Neither the name of 'Osmorc Development Team' nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.osmorc.frameworkintegration;
import com.intellij.openapi.vfs.VfsUtil;
import org.jetbrains.annotations.Nullable;
import org.osgi.framework.Constants;
import java.io.IOException;
import java.io.File;
import java.io.FileInputStream;
import java.util.WeakHashMap;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
/**
* This is a helper class which helps providing information about bundles that do not necessarily belong to the
* project.
* <p/>
* XXX: I am aware that we have BundleManifestImpl already for this, but this one depends on PsiFiles, which do not work
* for stuff outside the project.
*
* @author <a href="mailto:janthomae@janthomae.de">Jan Thomä</a>
* @version $Id:$
*/
public class CachingBundleInfoProvider {
private static final WeakHashMap<String, Manifest> _cache = new WeakHashMap<String, Manifest>();
private CachingBundleInfoProvider() {
}
/**
* Returns true if the file at the given url is a bundle, false otherwise.
*
* @param bundleUrl the url of the bundle
* @return true if the file at the url is a bundle, false otherwise.
*/
public static boolean isBundle(String bundleUrl) {
return getBundleSymbolicName(bundleUrl) != null;
}
/**
* Returns true if the object at the given URI can be bundlified. This is only true for jar files which are not already bundles.
* @param bundleUrl the url to the object to be bundlified.
* @return true if the object can be bundlified, false otherwise.
*/
public static boolean canBeBundlified(String bundleUrl) {
if (isBundle(bundleUrl)) return false;
File sourceFile = new File(VfsUtil.urlToPath(bundleUrl));
if (sourceFile.isDirectory()) {
// it's an exploded directory, we cannot bundle these.
return false;
}
if (!sourceFile.getName().endsWith(".jar")) {
// it's no jar, so we won't bundlify it either.
return false;
}
return true;
}
/**
* Returns the symbolic name of the bundle at the given url. If the file at the given url is no bundle, returns null.
*
* @param bundleUrl the url of the bundle
* @return the symbolic name of the bundle or null if the file is no bundle.
*/
@Nullable
public static String getBundleSymbolicName(String bundleUrl) {
String symbolicName = getBundleAttribute(bundleUrl, Constants.BUNDLE_SYMBOLICNAME);
return symbolicName != null ? symbolicName.split(";", 2)[0] : null; // Only take the name and leave the parameters
}
/**
* Returns the version of the bundle at the given url.
*
* @param bundleUrl the url of the bundle to search
* @return the version of the bundle or null if the file is no bundle
*/
@Nullable
public static String getBundleVersions(String bundleUrl) {
return getBundleAttribute(bundleUrl, Constants.BUNDLE_VERSION);
}
/**
* Returns boolean status if the given bundle is a fragment bundle.
*
* @param bundleUrl the url of the bundle
* @return true if the given bundle is a fragment bundle, false if it is not or if the state could not be determined.
*/
public static boolean isFragmentBundle(String bundleUrl) {
String fragmentHost = getBundleAttribute(bundleUrl, Constants.FRAGMENT_HOST);
return fragmentHost != null;
}
/**
* Returns the attribute of the bundle located at the given url. If the bundle cannot be found there or the jar at
* that location isn't a bundle, this returns null.
*
* @param bundleUrl the url of the bundle
* @param attribute the attribute to resolve
* @return the attribute's value or null if there is no such bundle or no such attribute
*/
@Nullable
private synchronized static String getBundleAttribute(String bundleUrl, String attribute) {
bundleUrl = normalize(bundleUrl);
if (!_cache.containsKey(bundleUrl)) {
try {
File bundleFile = new File(VfsUtil.urlToPath(bundleUrl));
if (bundleFile.isDirectory()) {
File manifestFile = new File(bundleFile, "META-INF/MANIFEST.MF");
if (manifestFile.exists() && !manifestFile.isDirectory()) {
FileInputStream fileInputStream = new FileInputStream(manifestFile);
Manifest manifest = new Manifest(fileInputStream);
fileInputStream.close();
_cache.put(bundleUrl, manifest);
}
}
else {
JarFile file = new JarFile(bundleFile);
_cache.put(bundleUrl, file.getManifest());
file.close();
}
}
catch (IOException e) {
return null;
}
}
Manifest manifest = _cache.get(bundleUrl);
return manifest != null ? manifest.getMainAttributes().getValue(attribute) : null;
}
private static String normalize(String bundleUrl) {
return bundleUrl.replaceAll("file:/([^/]+)", "file:///$1");
}
public static boolean isExploded(String bundleUrl) {
File bundleFile = new File(VfsUtil.urlToPath(bundleUrl));
return bundleFile.isDirectory();
}
}