/*
* 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.ivy.osgi.core;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.Iterator;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import org.apache.ivy.osgi.util.Version;
import org.apache.ivy.osgi.util.VersionRange;
/**
* Provides an OSGi manifest parser.
*
*/
public class ManifestParser {
private static final String EXPORT_PACKAGE = "Export-Package";
private static final String IMPORT_PACKAGE = "Import-Package";
private static final String EXPORT_SERVICE = "Export-Service";
private static final String IMPORT_SERVICE = "Import-Service";
private static final String REQUIRE_BUNDLE = "Require-Bundle";
private static final String BUNDLE_VERSION = "Bundle-Version";
private static final String BUNDLE_NAME = "Bundle-Name";
private static final String BUNDLE_DESCRIPTION = "Bundle-Description";
private static final String BUNDLE_SYMBOLIC_NAME = "Bundle-SymbolicName";
private static final String BUNDLE_MANIFEST_VERSION = "Bundle-ManifestVersion";
private static final String BUNDLE_REQUIRED_EXECUTION_ENVIRONMENT = "Bundle-RequiredExecutionEnvironment";
private static final String ATTR_RESOLUTION = "resolution";
private static final String ATTR_VERSION = "version";
private static final String ATTR_BUNDLE_VERSION = "bundle-version";
private static final String ATTR_USE = "use";
public static BundleInfo parseJarManifest(InputStream jarStream) throws IOException,
ParseException {
final JarInputStream jis = new JarInputStream(jarStream);
final BundleInfo parseManifest = parseManifest(jis.getManifest());
jis.close();
return parseManifest;
}
public static BundleInfo parseManifest(File manifestFile) throws IOException, ParseException {
final FileInputStream fis = new FileInputStream(manifestFile);
final BundleInfo parseManifest = parseManifest(fis);
fis.close();
return parseManifest;
}
public static BundleInfo parseManifest(InputStream manifestStream) throws IOException,
ParseException {
final BundleInfo parseManifest = parseManifest(new Manifest(manifestStream));
manifestStream.close();
return parseManifest;
}
public static BundleInfo parseManifest(Manifest manifest) throws ParseException {
Attributes mainAttributes = manifest.getMainAttributes();
String manifestVersion = mainAttributes.getValue(BUNDLE_MANIFEST_VERSION);
if (manifestVersion == null) {
// non OSGi manifest
throw new ParseException("No " + BUNDLE_MANIFEST_VERSION + " in the manifest", 0);
}
String symbolicName = new ManifestHeaderValue(mainAttributes.getValue(BUNDLE_SYMBOLIC_NAME))
.getSingleValue();
if (symbolicName == null) {
throw new ParseException("No " + BUNDLE_SYMBOLIC_NAME + " in the manifest", 0);
}
String description = new ManifestHeaderValue(mainAttributes.getValue(BUNDLE_DESCRIPTION))
.getSingleValue();
if (description == null) {
description = new ManifestHeaderValue(mainAttributes.getValue(BUNDLE_DESCRIPTION))
.getSingleValue();
}
String vBundle = new ManifestHeaderValue(mainAttributes.getValue(BUNDLE_VERSION))
.getSingleValue();
Version version;
try {
version = versionOf(vBundle);
} catch (NumberFormatException e) {
throw new ParseException("The " + BUNDLE_VERSION + " has an incorrect version: "
+ vBundle + " (" + e.getMessage() + ")", 0);
}
BundleInfo bundleInfo = new BundleInfo(symbolicName, version);
bundleInfo.setDescription(description);
List/* <String> */environments = new ManifestHeaderValue(
mainAttributes.getValue(BUNDLE_REQUIRED_EXECUTION_ENVIRONMENT)).getValues();
bundleInfo.setExecutionEnvironments(environments);
parseRequirement(bundleInfo, mainAttributes, REQUIRE_BUNDLE, BundleInfo.BUNDLE_TYPE,
ATTR_BUNDLE_VERSION);
parseRequirement(bundleInfo, mainAttributes, IMPORT_PACKAGE, BundleInfo.PACKAGE_TYPE,
ATTR_VERSION);
parseRequirement(bundleInfo, mainAttributes, IMPORT_SERVICE, BundleInfo.SERVICE_TYPE,
ATTR_VERSION);
ManifestHeaderValue exportElements = new ManifestHeaderValue(
mainAttributes.getValue(EXPORT_PACKAGE));
Iterator itExports = exportElements.getElements().iterator();
while (itExports.hasNext()) {
ManifestHeaderElement exportElement = (ManifestHeaderElement) itExports.next();
String vExport = (String) exportElement.getAttributes().get(ATTR_VERSION);
Version v = null;
try {
v = versionOf(vExport);
} catch (NumberFormatException e) {
throw new ParseException("The " + EXPORT_PACKAGE + " has an incorrect version: "
+ vExport + " (" + e.getMessage() + ")", 0);
}
Iterator itNames = exportElement.getValues().iterator();
while (itNames.hasNext()) {
String name = (String) itNames.next();
ExportPackage export = new ExportPackage(name, v);
String uses = (String) exportElement.getDirectives().get(ATTR_USE);
if (uses != null) {
String[] split = uses.trim().split(",");
for (int i = 0; i < split.length; i++) {
export.addUse(split[i].trim());
}
}
bundleInfo.addCapability(export);
}
}
parseCapability(bundleInfo, mainAttributes, EXPORT_SERVICE, BundleInfo.SERVICE_TYPE);
return bundleInfo;
}
private static void parseRequirement(BundleInfo bundleInfo, Attributes mainAttributes,
String headerName, String type, String versionAttr) throws ParseException {
ManifestHeaderValue elements = new ManifestHeaderValue(mainAttributes.getValue(headerName));
Iterator itElement = elements.getElements().iterator();
while (itElement.hasNext()) {
ManifestHeaderElement element = (ManifestHeaderElement) itElement.next();
String resolution = (String) element.getDirectives().get(ATTR_RESOLUTION);
String attVersion = (String) element.getAttributes().get(versionAttr);
VersionRange version = null;
try {
version = versionRangeOf(attVersion);
} catch (ParseException e) {
throw new ParseException("The " + headerName + " has an incorrect version: "
+ attVersion + " (" + e.getMessage() + ")", 0);
}
Iterator itNames = element.getValues().iterator();
while (itNames.hasNext()) {
String name = (String) itNames.next();
bundleInfo.addRequirement(new BundleRequirement(type, name, version, resolution));
}
}
}
private static void parseCapability(BundleInfo bundleInfo, Attributes mainAttributes,
String headerName, String type) throws ParseException {
ManifestHeaderValue elements = new ManifestHeaderValue(mainAttributes.getValue(headerName));
Iterator itElement = elements.getElements().iterator();
while (itElement.hasNext()) {
ManifestHeaderElement element = (ManifestHeaderElement) itElement.next();
String attVersion = (String) element.getAttributes().get(ATTR_VERSION);
Version version = null;
try {
version = versionOf(attVersion);
} catch (NumberFormatException e) {
throw new ParseException("The " + headerName + " has an incorrect version: "
+ attVersion + " (" + e.getMessage() + ")", 0);
}
Iterator itNames = element.getValues().iterator();
while (itNames.hasNext()) {
String name = (String) itNames.next();
BundleCapability export = new BundleCapability(type, name, version);
bundleInfo.addCapability(export);
}
}
}
private static VersionRange versionRangeOf(String v) throws ParseException {
if (v == null) {
return null;
}
return new VersionRange(v);
}
private static Version versionOf(String v) throws ParseException {
if (v == null) {
return null;
}
return new Version(v);
}
}