blob: 03300b5fe98dcc6ea4b8df9dd94b2f86c6a5ceb5 [file] [log] [blame]
package org.apache.felix.bundleplugin;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import aQute.bnd.service.AnalyzerPlugin;
import aQute.lib.osgi.Analyzer;
import aQute.lib.osgi.Jar;
import aQute.lib.osgi.Processor;
import aQute.lib.osgi.Resource;
import aQute.libg.generics.Create;
import aQute.libg.qtokens.QuotedTokenizer;
import aQute.libg.reporter.Reporter;
public class BlueprintPlugin implements AnalyzerPlugin {
static Pattern QN = Pattern.compile("[_A-Za-z$][_A-Za-z0-9$]*(\\.[_A-Za-z$][_A-Za-z0-9$]*)*");
static Pattern PATHS = Pattern.compile( ".*\\.xml");
Transformer transformer;
public BlueprintPlugin() throws Exception {
transformer = getTransformer(getClass().getResource("blueprint.xsl"));
}
public boolean analyzeJar(Analyzer analyzer) throws Exception {
transformer.setParameter("nsh_interface", analyzer.getProperty("nsh_interface") != null ? analyzer.getProperty("nsh_interface") : "");
transformer.setParameter("nsh_namespace", analyzer.getProperty("nsh_namespace") != null ? analyzer.getProperty("nsh_namespace") : "");
Set<String> headers = Create.set();
String bpHeader = analyzer.getProperty("Bundle-Blueprint", "OSGI-INF/blueprint");
Map<String, Map<String,String>> map = Processor.parseHeader(bpHeader, null);
for (String root : map.keySet())
{
Jar jar = analyzer.getJar();
Map<String, Resource> dir = jar.getDirectories().get(root);
if(dir == null || dir.isEmpty())
{
Resource resource = jar.getResource(root);
if(resource != null)
process(analyzer, root, resource, headers);
return false;
}
for(Map.Entry<String,Resource> entry : dir.entrySet())
{
String path = entry.getKey();
Resource resource = entry.getValue();
if(PATHS.matcher(path).matches())
process(analyzer, path, resource, headers);
}
}
// Group and analyze
Map<String, Set<Attribute>> hdrs = Create.map();
for (String str : headers) {
int idx = str.indexOf(':');
if (idx < 0)
{
analyzer.warning((new StringBuilder("Error analyzing services in blueprint resource: ")).append(str).toString());
continue;
}
String h = str.substring(0, idx).trim();
String v = str.substring(idx + 1).trim();
Set<Attribute> att = hdrs.get(h);
if (att == null) {
att = new TreeSet<Attribute>();
hdrs.put(h, att);
}
att.addAll(parseHeader(v, null));
}
// Merge
for (String header : hdrs.keySet())
{
if ("Import-Class".equals(header) || "Import-Package".equals(header))
{
Set<Attribute> newAttr = hdrs.get(header);
for (Attribute a : newAttr)
{
String pkg = a.getName();
if ("Import-Class".equals(header))
{
int n = a.getName().lastIndexOf('.');
if (n > 0) {
pkg = pkg.subSequence(0, n).toString();
} else {
continue;
}
}
if (!analyzer.getReferred().containsKey(pkg))
{
analyzer.getReferred().put(pkg, a.getProperties());
}
}
}
else
{
Set<Attribute> orgAttr = parseHeader(analyzer.getProperty(header), null);
Set<Attribute> newAttr = hdrs.get(header);
for (Iterator<Attribute> it = newAttr.iterator(); it.hasNext();)
{
Attribute a = it.next();
for (Attribute b : orgAttr)
{
if (b.getName().equals(a.getName()))
{
it.remove();
break;
}
}
}
orgAttr.addAll(newAttr);
// Rebuild from orgAttr
StringBuilder sb = new StringBuilder();
for (Attribute a : orgAttr)
{
if (sb.length() > 0)
{
sb.append(",");
}
sb.append(a.getName());
for (Map.Entry<String, String> prop : a.getProperties().entrySet())
{
sb.append(';').append(prop.getKey()).append("=");
if (prop.getValue().matches("[0-9a-zA-Z_-]+")) {
sb.append(prop.getValue());
} else {
sb.append("\"");
sb.append(prop.getValue().replace("\"", "\\\""));
sb.append("\"");
}
}
}
analyzer.setProperty(header, sb.toString());
}
}
return false;
}
private void process(Analyzer analyzer, String path, Resource resource, Set<String> headers)
{
InputStream in = null;
try
{
in = resource.openInputStream();
// Retrieve headers
Set<String> set = analyze(in);
headers.addAll(set);
}
catch(Exception e)
{
analyzer.error((new StringBuilder("Unexpected exception in processing spring resources(")).append(path).append("): ").append(e).toString());
}
finally
{
try
{
if (in != null)
{
in.close();
}
}
catch (IOException e)
{
}
}
}
public Set<String> analyze(InputStream in)
throws Exception
{
Set<String> refers = new HashSet<String>();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
javax.xml.transform.Result r = new StreamResult(bout);
javax.xml.transform.Source s = new StreamSource(in);
transformer.transform(s, r);
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
bout.close();
BufferedReader br = new BufferedReader(new InputStreamReader(bin));
for(String line = br.readLine(); line != null; line = br.readLine())
{
line = line.trim();
line = line.replace(";availability:=mandatory", "");
if(line.length() > 0)
{
refers.add(line);
}
}
br.close();
return refers;
}
protected Transformer getTransformer(URL url)
throws Exception
{
TransformerFactory tf = TransformerFactory.newInstance();
javax.xml.transform.Source source = new StreamSource(url.openStream());
return tf.newTransformer(source);
}
public static class Attribute implements Comparable<Attribute>
{
private final String name;
private final Map<String,String> properties;
public Attribute(String name, Map<String, String> properties) {
this.name = name;
this.properties = properties;
}
public String getName() {
return name;
}
public Map<String, String> getProperties() {
return properties;
}
public int compareTo(Attribute a) {
int c = name.compareTo(a.name);
if (c == 0)
{
c = properties.equals(a.properties) ? 0 :
properties.size() < a.properties.size() ? -1 :
properties.hashCode() < a.properties.hashCode() ? -1 : +1;
}
return c;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Attribute attribute = (Attribute) o;
if (name != null ? !name.equals(attribute.name) : attribute.name != null) return false;
if (properties != null ? !properties.equals(attribute.properties) : attribute.properties != null)
return false;
return true;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (properties != null ? properties.hashCode() : 0);
return result;
}
}
public static Set<Attribute> parseHeader(String value, Reporter logger)
{
if ((value == null) || (value.trim().length() == 0)) {
return new TreeSet<Attribute>();
}
Set<Attribute> result = new TreeSet<Attribute>();
QuotedTokenizer qt = new QuotedTokenizer(value, ";=,");
char del = '\0';
do {
boolean hadAttribute = false;
Map<String,String> clause = Create.map();
List<String> aliases = Create.list();
String name = qt.nextToken(",;");
del = qt.getSeparator();
if ((name == null) || (name.length() == 0)) {
if ((logger != null) && (logger.isPedantic())) {
logger.warning("Empty clause, usually caused by repeating a comma without any name field or by having " +
"spaces after the backslash of a property file: " +
value);
}
if (name != null)
continue;
break;
}
name = name.trim();
aliases.add(name);
String advalue;
while (del == ';') {
String adname = qt.nextToken();
if ((del = qt.getSeparator()) != '=') {
if ((hadAttribute) && (logger != null)) {
logger.error("Header contains name field after attribute or directive: " +
adname +
" from " +
value +
". Name fields must be consecutive, separated by a ';' like a;b;c;x=3;y=4");
}
if ((adname != null) && (adname.length() > 0))
aliases.add(adname.trim());
} else {
advalue = qt.nextToken();
if ((clause.containsKey(adname)) && (logger != null) && (logger.isPedantic())) {
logger.warning("Duplicate attribute/directive name " +
adname +
" in " +
value +
". This attribute/directive will be ignored");
}
if (advalue == null) {
if (logger != null) {
logger.error("No value after '=' sign for attribute " + adname);
}
advalue = "";
}
clause.put(adname.trim(), advalue.trim());
del = qt.getSeparator();
hadAttribute = true;
}
}
for (String clauseName : aliases) {
result.add(new Attribute(clauseName, clause));
}
}
while (del == ',');
return result;
}
}