/**********************************************************************
* Copyright (c) 2005-2009 ant4eclipse project team.
*
* 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:
* Nils Hartmann, Daniel Kasmeroglu, Gerd Wuetherich
**********************************************************************/
package org.ant4eclipse.lib.core.util;
import java.util.LinkedList;
import java.util.List;
import java.util.jar.Manifest;
import org.ant4eclipse.lib.core.Assure;
import org.ant4eclipse.lib.core.CoreExceptionCode;
import org.ant4eclipse.lib.core.exception.Ant4EclipseException;
/**
* <p>
* </p>
*
* @author Gerd Wütherich (gerd@gerd-wuetherich.de)
*/
public class ManifestHelper {
/**
* Manifest header (named "Bundle-ClassPath") identifying a list of directories and embedded JAR files,
* which are bundle resources used to extend the bundle's classpath.
*
* <p>
* The attribute value may be retrieved from the <code>Dictionary</code> object returned by the
* <code>Bundle.getHeaders</code> method.
*/
public static final String BUNDLE_CLASSPATH = "Bundle-ClassPath";
/**
* Manifest header (named "Export-Package") identifying the packages that the bundle offers to the Framework
* for export.
*
* <p>
* The attribute value may be retrieved from the <code>Dictionary</code> object returned by the
* <code>Bundle.getHeaders</code> method.
*/
public static final String EXPORT_PACKAGE = "Export-Package";
/**
* Manifest header (named "Bundle-Version") identifying the bundle's version.
*
* <p>
* The attribute value may be retrieved from the <code>Dictionary</code> object returned by the
* <code>Bundle.getHeaders</code> method.
*/
public static final String BUNDLE_VERSION = "Bundle-Version";
/**
* Manifest header (named "Bundle-SymbolicName") identifying the bundle's symbolic name.
* <p>
* The attribute value may be retrieved from the <code>Dictionary</code> object returned by the
* <code>Bundle.getHeaders</code> method.
*/
public static final String BUNDLE_SYMBOLICNAME = "Bundle-SymbolicName";
/**
* <p>
* Returns the 'Bundle-SymbolicName' entry for the given manifest. If not such entry exists, an Exception will be
* thrown.
* </p>
*
* @param manifest
* the manifest
*
* @return The symbolic name.
*
* @throws Ant4EclipseException
* if the symbolic name could not be found.
*/
public static String getBundleSymbolicName(Manifest manifest) {
Assure.notNull("manifest", manifest);
// get the manifest header elements
ManifestHelper.ManifestHeaderElement[] elements = ManifestHelper.getManifestHeaderElements(manifest,
ManifestHelper.BUNDLE_SYMBOLICNAME);
// assert that entry exists
if (elements == null || elements.length == 0 || elements[0].getValues() == null
|| elements[0].getValues().length == 0) {
throw new Ant4EclipseException(CoreExceptionCode.MANIFEST_HEADER_DOES_NOT_EXIST,
ManifestHelper.BUNDLE_SYMBOLICNAME);
}
// return the symbolic name
return elements[0].getValues()[0];
}
/**
* <p>
* Returns the 'Bundle-Classpath' entries or <code>.</code> if no 'Bundle-Classpath' has been specified (default
* value).
* </p>
*
* @param manifest
* The manifest providing the necessary information. Not <code>null</code>.
*
* @return the 'Bundle-Classpath' entries or <code>.</code> if no 'Bundle-Classpath' has been specified.
*/
public static String[] getBundleClasspath(Manifest manifest) {
// parse the 'Bundle-Classpath' manifest entry
String[] bundleClasspath = splitHeader(manifest.getMainAttributes().getValue(BUNDLE_CLASSPATH));
// set default if necessary
if ((bundleClasspath == null) || (bundleClasspath.length < 1)) {
bundleClasspath = new String[] { "." };
} else {
for (int i = 0; i < bundleClasspath.length; i++) {
bundleClasspath[i] = bundleClasspath[i].trim();
}
}
// return result
return bundleClasspath;
}
public static String getManifestHeader(Manifest manifest, String header) {
Assure.notNull("manifest", manifest);
Assure.nonEmpty("header", header);
return manifest.getMainAttributes().getValue(header);
}
public static ManifestHeaderElement[] getManifestHeaderElements(Manifest manifest, String header) {
Assure.notNull("manifest", manifest);
Assure.nonEmpty("header", header);
String manifestValue = manifest.getMainAttributes().getValue(header);
if ((manifestValue == null) || "".equals(manifestValue.trim())) {
return new ManifestHeaderElement[0];
}
return getManifestHeaderElements(manifestValue);
}
public static ManifestHeaderElement[] getManifestHeaderElements(String manifestValue) {
Assure.nonEmpty("manifestValue", manifestValue);
String[] elements = splitHeader(manifestValue);
List<ManifestHeaderElement> result = new LinkedList<ManifestHeaderElement>();
for (String element : elements) {
ManifestHeaderElement manifestHeaderElement = new ManifestHeaderElement();
result.add(manifestHeaderElement);
String[] elementParts = splitHeader(element, ";");
for (String elementPart : elementParts) {
String[] splitted = splitHeader(elementPart, ":=");
// Directive found...
if (splitted.length > 1) {
manifestHeaderElement.addDirective(splitted[0], removeQuotes(splitted[1]));
} else {
splitted = splitHeader(elementPart, "=");
if (splitted.length > 1) {
manifestHeaderElement.addAttribute(splitted[0], removeQuotes(splitted[1]));
} else {
manifestHeaderElement.addValue(elementPart);
}
}
}
}
return result.toArray(new ManifestHeaderElement[0]);
}
/**
* @param header
* @return
*/
public static String[] splitHeader(String header) {
return splitHeader(header, ",");
}
/**
* @param header
* @param separator
* @return
*/
public static String[] splitHeader(String header, String separator) {
if ((header == null) || (header.trim().length() == 0)) {
return new String[0];
}
List<String> result = new LinkedList<String>();
char[] chars = header.toCharArray();
StringBuilder currentValue = new StringBuilder();
boolean inQuotedString = false;
for (int i = 0; i < chars.length; i++) {
// separator string is not found:
// - append char to current value
// - check for the beginning or ending of quoted strings
if (!lookup(chars, i, separator)) {
// single '"' found: quoted string begins or ends
if (lookup(chars, i, "\"")) {
inQuotedString = !inQuotedString;
}
// filter for '\"'
else if (lookup(chars, i, "\\\"")) {
currentValue.append(chars[i]);
i++;
}
// append char to current value
currentValue.append(chars[i]);
}
// separator char found:
// - split if we are not in a quoted string
// - no split if we are
else {
// - split if we are not in a quoted string
if (!inQuotedString) {
result.add(currentValue.toString());
currentValue = new StringBuilder();
i = i + (separator.length() - 1);
}
// - no split if we are in a quoted string
else {
currentValue.append(chars[i]);
}
}
}
// add the current value
result.add(currentValue.toString());
return result.toArray(new String[0]);
}
/**
* @param array
* @param index
* @param pattern
* @return
*/
private static boolean lookup(char[] array, int index, String pattern) {
Assure.nonEmpty("pattern", pattern);
if (index + pattern.length() > array.length) {
return false;
}
char[] patternChars = pattern.toCharArray();
for (int i = 0; i < patternChars.length; i++) {
if (array[index + i] != patternChars[i]) {
return false;
}
}
return true;
}
private static String removeQuotes(String value) {
if (value == null) {
return null;
}
String result = value;
if (result.startsWith("\"")) {
result = result.substring(1);
}
if (result.endsWith("\"")) {
result = result.substring(0, result.length() - 1);
}
return result;
}
/**
* ManifestHeaderElement --
*
* @author Gerd W¨,therich (gerd@gerd-wuetherich.de)
*/
public static class ManifestHeaderElement {
/** the values */
protected List<String> _values;
/** the attributes */
protected StringMap _attributes;
/** the directives */
protected StringMap _directives;
public ManifestHeaderElement() {
this._values = new LinkedList<String>();
this._attributes = new StringMap();
this._directives = new StringMap();
}
void addAttribute(String key, String value) {
this._attributes.put(key, value);
}
void addDirective(String key, String value) {
this._directives.put(key, value);
}
public boolean addValue(String o) {
return this._values.add(o);
}
/**
* @return
*/
public String[] getValues() {
return this._values.toArray(new String[0]);
}
/**
* @return
*/
public StringMap getAttributes() {
return this._attributes;
}
/**
* @return
*/
public StringMap getDirectives() {
return this._directives;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("[ManifestHeaderElement:");
buffer.append(" _values: ");
buffer.append(this._values);
buffer.append(" _attributes: ");
buffer.append(this._attributes);
buffer.append(" _directives: ");
buffer.append(this._directives);
buffer.append("]");
return buffer.toString();
}
}
}