blob: 1edee69843e1b3fab31c1a86ce7464854c6a6820 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2010 VMware Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware Inc. - initial contribution
*******************************************************************************/
package org.eclipse.virgo.util.osgi.manifest.internal;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import org.eclipse.virgo.util.common.CaseInsensitiveMap;
import org.eclipse.virgo.util.common.MapToDictionaryAdapter;
import org.eclipse.virgo.util.osgi.manifest.BundleActivationPolicy;
import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
import org.eclipse.virgo.util.osgi.manifest.BundleSymbolicName;
import org.eclipse.virgo.util.osgi.manifest.DynamicImportPackage;
import org.eclipse.virgo.util.osgi.manifest.ExportPackage;
import org.eclipse.virgo.util.osgi.manifest.FragmentHost;
import org.eclipse.virgo.util.osgi.manifest.ImportBundle;
import org.eclipse.virgo.util.osgi.manifest.ImportLibrary;
import org.eclipse.virgo.util.osgi.manifest.ImportPackage;
import org.eclipse.virgo.util.osgi.manifest.Parseable;
import org.eclipse.virgo.util.osgi.manifest.RequireBundle;
import org.eclipse.virgo.util.osgi.manifest.parse.HeaderParser;
import org.eclipse.virgo.util.osgi.manifest.parse.ParserLogger;
import org.eclipse.virgo.util.osgi.manifest.parse.standard.StandardHeaderParser;
import org.eclipse.virgo.util.parser.manifest.ManifestContents;
import org.eclipse.virgo.util.parser.manifest.ManifestParser;
import org.eclipse.virgo.util.parser.manifest.ManifestProblem;
import org.eclipse.virgo.util.parser.manifest.ManifestProblemKind;
import org.eclipse.virgo.util.parser.manifest.RecoveringManifestParser;
import org.osgi.framework.Constants;
import org.osgi.framework.Version;
/**
* <strong>Concurrent Semantics</strong><br />
*
* Not thread-safe.
*/
public class StandardBundleManifest implements BundleManifest {
private static final String MANIFEST_VERSION = "Manifest-Version";
static final String MANIFEST_VERSION_VALUE = "1.0";
private final CaseInsensitiveMap<String> contents = new CaseInsensitiveMap<String>();
private final CaseInsensitiveMap<Parseable> headers;
public StandardBundleManifest(ParserLogger logger) {
this(logger, (Map<String, String>) new Hashtable<String, String>());
}
public StandardBundleManifest(ParserLogger logger, Map<String, String> contents) {
this.contents.putAll(contents);
if (!this.contents.containsKey(MANIFEST_VERSION)) {
this.contents.put(MANIFEST_VERSION, MANIFEST_VERSION_VALUE);
}
this.headers = initializeHeaders(this.contents, new StandardHeaderParser(logger));
}
public StandardBundleManifest(ParserLogger logger, Dictionary<String, String> contents) {
this(logger, dictionaryToMap(contents));
}
private static Map<String, String> dictionaryToMap(Dictionary<String, String> contents) {
Map<String, String> map = new HashMap<String, String>(contents.size());
Enumeration<String> keys = contents.keys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
map.put(key, contents.get(key));
}
return map;
}
public StandardBundleManifest(ParserLogger logger, Reader reader) throws IOException {
this(logger, parseContents(reader));
}
public StandardBundleManifest(ParserLogger logger, ManifestContents manifestContents) {
this(logger, manifestContents.getMainAttributes());
}
private static ManifestContents parseContents(Reader reader) throws IOException {
ManifestParser parser = new RecoveringManifestParser();
parser.setTerminateAfterMainSection(true);
ManifestContents contents = parser.parse(reader);
if (parser.foundProblems()) {
List<ManifestProblem> problems = parser.getProblems();
for (ManifestProblem problem : problems) {
if (!problem.getKind().equals(ManifestProblemKind.ILLEGAL_NAME_CHAR) && !problem.getKind().equals(ManifestProblemKind.VALUE_TOO_LONG)) {
throw new IOException(problems.toString());
}
}
}
return contents;
}
private static CaseInsensitiveMap<Parseable> initializeHeaders(Map<String, String> contents, HeaderParser parser) {
CaseInsensitiveMap<Parseable> headers = createHeadersMap(parser);
for (Map.Entry<String, Parseable> entry : headers.entrySet()) {
String value = contents.get(entry.getKey());
if (value != null) {
entry.getValue().resetFromParseString(value);
}
}
return headers;
}
private static CaseInsensitiveMap<Parseable> createHeadersMap(HeaderParser parser) {
CaseInsensitiveMap<Parseable> headers = new CaseInsensitiveMap<Parseable>();
headers.put(Constants.BUNDLE_ACTIVATIONPOLICY, new StandardBundleActivationPolicy(parser));
headers.put(Constants.BUNDLE_SYMBOLICNAME, new StandardBundleSymbolicName(parser));
headers.put(Constants.DYNAMICIMPORT_PACKAGE, new StandardDynamicImportPackage(parser));
headers.put(Constants.EXPORT_PACKAGE, new StandardExportPackage(parser));
headers.put(Constants.FRAGMENT_HOST, new StandardFragmentHost(parser));
headers.put(IMPORT_BUNDLE, new StandardImportBundle(parser));
headers.put(IMPORT_LIBRARY, new StandardImportLibrary(parser));
headers.put(Constants.IMPORT_PACKAGE, new StandardImportPackage(parser));
headers.put(Constants.REQUIRE_BUNDLE, new StandardRequireBundle(parser));
return headers;
}
/**
* {@inheritDoc}
*/
public BundleActivationPolicy getBundleActivationPolicy() {
return (BundleActivationPolicy) this.headers.get(Constants.BUNDLE_ACTIVATIONPOLICY);
}
/**
* {@inheritDoc}
*/
public List<String> getBundleClasspath() {
return HeaderUtils.toList(Constants.BUNDLE_CLASSPATH, this.contents);
}
/**
* {@inheritDoc}
*/
public String getBundleDescription() {
return this.contents.get(Constants.BUNDLE_DESCRIPTION);
}
/**
* {@inheritDoc}
*/
public int getBundleManifestVersion() {
int bundleManifestVersion = 1;
String value = this.contents.get(Constants.BUNDLE_MANIFESTVERSION);
if (value != null) {
bundleManifestVersion = Integer.parseInt(value);
}
return bundleManifestVersion;
}
/**
* {@inheritDoc}
*/
public String getBundleName() {
return this.contents.get(Constants.BUNDLE_NAME);
}
/**
* {@inheritDoc}
*/
public BundleSymbolicName getBundleSymbolicName() {
return (BundleSymbolicName) this.headers.get(Constants.BUNDLE_SYMBOLICNAME);
}
/**
* {@inheritDoc}
*/
public URL getBundleUpdateLocation() {
URL updateLocation = null;
String value = this.contents.get(Constants.BUNDLE_UPDATELOCATION);
if (value != null) {
try {
updateLocation = new URL(value);
} catch (MalformedURLException murle) {
// Should never happen
}
}
return updateLocation;
}
/**
* {@inheritDoc}
*/
public DynamicImportPackage getDynamicImportPackage() {
return (DynamicImportPackage) this.headers.get(Constants.DYNAMICIMPORT_PACKAGE);
}
/**
* {@inheritDoc}
*/
public ExportPackage getExportPackage() {
return (ExportPackage) this.headers.get(Constants.EXPORT_PACKAGE);
}
/**
* {@inheritDoc}
*/
public FragmentHost getFragmentHost() {
return (FragmentHost) this.headers.get(Constants.FRAGMENT_HOST);
}
/**
* {@inheritDoc}
*/
public ImportBundle getImportBundle() {
return (ImportBundle) this.headers.get(BundleManifest.IMPORT_BUNDLE);
}
/**
* {@inheritDoc}
*/
public ImportLibrary getImportLibrary() {
return (ImportLibrary) this.headers.get(BundleManifest.IMPORT_LIBRARY);
}
/**
* {@inheritDoc}
*/
public ImportPackage getImportPackage() {
return (ImportPackage) this.headers.get(Constants.IMPORT_PACKAGE);
}
/**
* {@inheritDoc}
*/
public String getModuleScope() {
return this.contents.get(MODULE_SCOPE);
}
/**
* {@inheritDoc}
*/
public String getModuleType() {
return this.contents.get(MODULE_TYPE);
}
/**
* {@inheritDoc}
*/
public RequireBundle getRequireBundle() {
return (RequireBundle) this.headers.get(Constants.REQUIRE_BUNDLE);
}
/**
* {@inheritDoc}
*/
public void setBundleDescription(String bundleDescription) {
if (bundleDescription == null) {
this.contents.remove(Constants.BUNDLE_DESCRIPTION);
} else {
this.contents.put(Constants.BUNDLE_DESCRIPTION, bundleDescription);
}
}
/**
* {@inheritDoc}
*/
public void setBundleManifestVersion(int bundleManifestVersion) {
this.contents.put(Constants.BUNDLE_MANIFESTVERSION, Integer.toString(bundleManifestVersion));
}
/**
* {@inheritDoc}
*/
public void setBundleName(String bundleName) {
if (bundleName == null) {
this.contents.remove(Constants.BUNDLE_NAME);
} else {
this.contents.put(Constants.BUNDLE_NAME, bundleName);
}
}
/**
* {@inheritDoc}
*/
public void setBundleUpdateLocation(URL bundleUpdateLocation) {
if (bundleUpdateLocation == null) {
this.contents.remove(Constants.BUNDLE_UPDATELOCATION);
} else {
this.contents.put(Constants.BUNDLE_UPDATELOCATION, bundleUpdateLocation.toExternalForm());
}
}
/**
* {@inheritDoc}
*/
public void setModuleScope(String moduleScope) {
if (moduleScope == null) {
this.contents.remove(MODULE_SCOPE);
} else {
this.contents.put(MODULE_SCOPE, moduleScope);
}
}
/**
* {@inheritDoc}
*/
public void setModuleType(String moduleType) {
if (moduleType == null) {
this.contents.remove(MODULE_TYPE);
} else {
this.contents.put(MODULE_TYPE, moduleType);
}
}
/**
* {@inheritDoc}
*/
public Dictionary<String, String> toDictionary() {
synchroniseContentsWithHeaders();
return new MapToDictionaryAdapter(new CaseInsensitiveMap<String>(this.contents));
}
/**
* {@inheritDoc}
*/
public Version getBundleVersion() {
String value = this.contents.get(Constants.BUNDLE_VERSION);
if (value != null) {
return new Version(value);
} else {
return Version.emptyVersion;
}
}
/**
* {@inheritDoc}
*/
public void setBundleVersion(Version bundleVersion) {
if (bundleVersion != null) {
this.contents.put(Constants.BUNDLE_VERSION, bundleVersion.toString());
} else {
this.contents.remove(Constants.BUNDLE_VERSION);
}
}
/**
* {@inheritDoc}
*/
public String getHeader(String name) {
synchroniseContentsWithHeaders();
return this.contents.get(name);
}
private void synchroniseContentsWithHeaders() {
for (Map.Entry<String, String> content : this.contents.entrySet()) {
Parseable header = this.headers.get(content.getKey());
if (header != null) {
String headerValue = header.toParseString();
if (headerValue != null) {
this.contents.put(content.getKey(), headerValue);
} else {
this.contents.remove(content.getKey());
}
}
}
for (Map.Entry<String, Parseable> header : this.headers.entrySet()) {
if (this.contents.get(header.getKey()) == null) {
String headerValue = header.getValue().toParseString();
if (headerValue != null) {
this.contents.put(header.getKey(), headerValue);
}
}
}
}
/**
* {@inheritDoc}
*/
public void setHeader(String name, String value) {
if (value == null) {
this.contents.remove(name);
} else {
this.contents.put(name, value);
}
Parseable header = this.headers.get(name);
if (header != null) {
header.resetFromParseString(value);
}
}
public void write(Writer writer) throws IOException {
Manifest manifest = new Manifest();
Attributes attributes = manifest.getMainAttributes();
Dictionary<String, String> dictionary = toDictionary();
Enumeration<String> keys = dictionary.keys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
attributes.putValue(key, dictionary.get(key));
}
OutputStream writerOutputStream = new WriterOutputStream(writer);
try {
manifest.write(writerOutputStream);
} finally {
writerOutputStream.close();
}
}
public static class WriterOutputStream extends OutputStream {
private final Writer writer;
public WriterOutputStream(Writer writer) {
this.writer = writer;
}
@Override
public void write(int b) throws IOException {
this.writer.write(b);
}
@Override
public void close() throws IOException {
this.writer.close();
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
StringWriter outputManifest = new StringWriter();
try {
write(outputManifest);
} catch (Exception e) {
// No Exceptions here
}
return outputManifest.toString();
}
}