org.apache.aries.util.manifest.ManifestHeaderProcessor Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
/*
* 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 WARRANTIESOR 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.aries.util.manifest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.aries.util.ManifestHeaderUtils;
import org.apache.aries.util.VersionRange;
import org.osgi.framework.Constants;
import org.osgi.framework.Version;
public class ManifestHeaderProcessor
{
public static final String NESTED_FILTER_ATTRIBUTE = "org.apache.aries.application.filter.attribute";
private static final Pattern FILTER_ATTR = Pattern.compile("(\\(!)?\\((.*?)([<>]?=)(.*?)\\)\\)?");
private static final String LESS_EQ_OP = "<=";
private static final String GREATER_EQ_OP = ">=";
/**
* A GenericMetadata is either a Generic Capability or a Generic Requirement
*/
public static class GenericMetadata {
private final String namespace;
private final Map attributes = new HashMap();
private final Map directives = new HashMap();
public GenericMetadata(String namespace) {
this.namespace = namespace;
}
public String getNamespace() {
return namespace;
}
public Map getAttributes() {
return attributes;
}
public Map getDirectives() {
return directives;
}
}
/**
* A simple class to associate two types.
*/
public static class NameValuePair {
private String name;
private Map attributes;
public NameValuePair(String name, Map value)
{
this.name = name;
this.attributes = value;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public Map getAttributes()
{
return attributes;
}
public void setAttributes(Map value)
{
this.attributes = value;
}
@Override
public String toString(){
return "{"+name.toString()+"::"+attributes.toString()+"}";
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((attributes == null) ? 0 : attributes.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
final NameValuePair other = (NameValuePair) obj;
if (name == null) {
if (other.name != null) return false;
} else if (!name.equals(other.name)) return false;
if (attributes == null) {
if (other.attributes != null) return false;
} else if (!attributes.equals(other.attributes)) return false;
return true;
}
}
/**
* Intended to provide a standard way to add Name/Value's to
* aggregations of Name/Value's.
*/
public static interface NameValueCollection {
/**
* Add this Name & Value to the collection.
* @param n
* @param v
*/
public void addToCollection(String n, Map v);
}
/**
* Map of Name -> Value.
*/
public static class NameValueMap extends HashMap> implements NameValueCollection, Map>{
private static final long serialVersionUID = -6446338858542599141L;
public void addToCollection(String n, Map v){
this.put(n,v);
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("{");
boolean first=true;
for(Map.Entry> entry : this.entrySet()){
if(!first)sb.append(",");
first=false;
sb.append(entry.getKey()+"->"+entry.getValue());
}
sb.append("}");
return sb.toString();
}
}
/**
* List of Name/Value
*/
public static class NameValueList extends ArrayList implements NameValueCollection, List {
private static final long serialVersionUID = 1808636823825029983L;
public void addToCollection(String n, Map v){
this.add(new NameValuePair(n,v));
}
@Override
public String toString(){
StringBuffer sb = new StringBuffer();
sb.append("{");
boolean first = true;
for(NameValuePair nvp : this){
if(!first)sb.append(",");
first=false;
sb.append(nvp.toString());
}
sb.append("}");
return sb.toString();
}
}
/**
*
* Splits a delimiter separated string, tolerating presence of non separator commas
* within double quoted segments.
*
* Eg.
* com.ibm.ws.eba.helloWorldService;version="[1.0.0, 1.0.0]" &
* com.ibm.ws.eba.helloWorldService;version="1.0.0"
* com.ibm.ws.eba.helloWorld;version="2";bundle-version="[2,30)"
* com.acme.foo;weirdAttr="one;two;three";weirdDir:="1;2;3"
* @param value the value to be split
* @param delimiter the delimiter string such as ',' etc.
* @return List the components of the split String in a list
*/
public static List split(String value, String delimiter)
{
return ManifestHeaderUtils.split(value, delimiter);
}
/**
* Internal method to parse headers with the format
* [Name](;[Name])*(;[attribute-name]=[attribute-value])*
* Eg.
* rumplestiltskin;thing=value;other=something
* littleredridinghood
* bundle1;bundle2;other=things
* bundle1;bundle2
*
* @param s data to parse
* @return a list of NameValuePair, with the Name being the name component,
* and the Value being a NameValueMap of key->value mappings.
*/
private static List genericNameWithNameValuePairProcess(String s){
String name;
Map params = null;
List nameValues = new ArrayList();
List pkgs = new ArrayList();
int index = s.indexOf(";");
if(index==-1){
name = s;
params = new HashMap();
pkgs.add(name);
}else{
name = s.substring(0,index).trim();
String tail = s.substring(index+1).trim();
pkgs.add(name); // add the first package
StringBuilder parameters = new StringBuilder();
// take into consideration of multiple packages separated by ';'
// while they share the same attributes or directives
List tailParts = split(tail, ";");
boolean firstParameter =false;
for (String part : tailParts) {
// if it is not a parameter and no parameter appears in front of it, it must a package
if (!!!(part.contains("="))) {
// Need to make sure no parameter appears before the package, otherwise ignore this string
// as this syntax is invalid
if (!!!(firstParameter))
pkgs.add(part);
} else {
if (!!!(firstParameter))
firstParameter = true;
parameters.append(part + ";");
}
}
if (parameters.length() != 0) {
//remove the final ';' if there is one
if (parameters.toString().endsWith(";")) {
parameters = parameters.deleteCharAt(parameters.length() -1);
}
params = genericNameValueProcess(parameters.toString());
}
}
for (String pkg : pkgs) {
nameValues.add(new NameValuePair(pkg,params));
}
return nameValues;
}
/**
* Internal method to parse headers with the format
* [attribute-name]=[attribute-value](;[attribute-name]=[attribute-value])*
* Eg.
* thing=value;other=something
*
* Note. Directives (name:=value) are represented in the map with name suffixed by ':'
*
* @param s data to parse
* @return a NameValueMap, with attribute-name -> attribute-value.
*/
private static Map genericNameValueProcess(String s){
Map params = new HashMap();
List parameters = split(s, ";");
for(String parameter : parameters) {
List parts = split(parameter,"=");
// do a check, otherwise we might get NPE
if (parts.size() ==2) {
String second = parts.get(1).trim();
if (second.startsWith("\"") && second.endsWith("\""))
second = second.substring(1,second.length()-1);
String first = parts.get(0).trim();
// make sure for directives we clear out any space as in "directive :=value"
if (first.endsWith(":")) {
first = first.substring(0, first.length()-1).trim()+":";
}
params.put(first, second);
}
}
return params;
}
/**
* Processes an import/export style header..
* pkg1;attrib=value;attrib=value,pkg2;attrib=value,pkg3;attrib=value
*
* @param out The collection to add each package name + attrib map to.
* @param s The data to parse
*/
private static void genericImportExportProcess(NameValueCollection out, String s){
List packages = split(s, ",");
for(String pkg : packages){
List ps = genericNameWithNameValuePairProcess(pkg);
for (NameValuePair p : ps) {
out.addToCollection(p.getName(), p.getAttributes());
}
}
}
/**
* Parse an export style header.
* pkg1;attrib=value;attrib=value,pkg2;attrib=value,pkg3;attrib=value2
*
* Result is returned as a list, as export does allow duplicate package exports.
*
* @param s The data to parse.
* @return List of NameValuePairs, where each Name in the list is an exported package,
* with its associated Value being a NameValueMap of any attributes declared.
*/
public static List parseExportString(String s){
NameValueList retval = new NameValueList();
genericImportExportProcess(retval, s);
return retval;
}
/**
* Parse an export style header in a list.
* pkg1;attrib=value;attrib=value
* pkg2;attrib=value
* pkg3;attrib=value2
*
* Result is returned as a list, as export does allow duplicate package exports.
*
* @param list The data to parse.
* @return List of NameValuePairs, where each Name in the list is an exported package,
* with its associated Value being a NameValueMap of any attributes declared.
*/
public static List parseExportList(List list){
NameValueList retval = new NameValueList();
for(String pkg : list){
List ps = genericNameWithNameValuePairProcess(pkg);
for (NameValuePair p : ps) {
retval.addToCollection(p.getName(), p.getAttributes());
}
}
return retval;
}
/**
* Parse an import style header.
* pkg1;attrib=value;attrib=value,pkg2;attrib=value,pkg3;attrib=value
*
* Result is returned as a set, as import does not allow duplicate package imports.
*
* @param s The data to parse.
* @return Map of NameValuePairs, where each Key in the Map is an imported package,
* with its associated Value being a NameValueMap of any attributes declared.
*/
public static Map> parseImportString(String s){
NameValueMap retval = new NameValueMap();
genericImportExportProcess(retval, s);
return retval;
}
/**
* Parse a generic capability header. For example
* com.acme.myns;mylist:List="nl,be,fr,uk";myver:Version=1.3;long:Long="1234";d:Double="3.14";myattr=xyz,
* com.acme.myns;myattr=abc
* @param s The header to be parsed
* @return A list of GenericMetadata objects each representing an individual capability. The values in the attribute map
* are of the specified datatype.
*/
public static List parseCapabilityString(String s) {
return parseGenericMetadata(s);
}
/**
* Parse a generic capability header. For example
* com.acme.myns;mylist:List="nl,be,fr,uk";myver:Version=1.3;long:Long="1234";d:Double="3.14";myattr=xyz,
* com.acme.myns;myattr=abc
* @param s The header to be parsed
* @return A list of GenericMetadata objects each representing an individual capability. The values in the attribute map
* are of the specified datatype.
*/
public static List parseRequirementString(String s) {
return parseGenericMetadata(s);
}
private static List parseGenericMetadata(String s) {
List capabilities = new ArrayList();
List entries = split(s, ",");
for(String e : entries){
List nvpList = genericNameWithNameValuePairProcess(e);
for(NameValuePair nvp : nvpList) {
String namespace = nvp.getName();
GenericMetadata cap = new GenericMetadata(namespace);
capabilities.add(cap);
Map attrMap = nvp.getAttributes();
for (Map.Entry entry : attrMap.entrySet()) {
String k = entry.getKey();
String v = entry.getValue();
if (k.contains(":")) {
if (k.endsWith(":")) {
// a directive
cap.getDirectives().put(k.substring(0, k.length() - 1), v);
} else {
// an attribute with its datatype specified
parseTypedAttribute(k, v, cap);
}
} else {
// ordinary (String) attribute
cap.getAttributes().put(k, v);
}
}
}
}
return capabilities;
}
private static void parseTypedAttribute(String k, String v, GenericMetadata cap) {
int idx = k.indexOf(':');
String name = k.substring(0, idx);
String type = k.substring(idx + 1);
if (type.startsWith("List<") && type.endsWith(">")) {
String subtype = type.substring("List<".length(), type.length() - 1).trim();
List