package aQute.bnd.metatype;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import aQute.bnd.component.TagResource;
import aQute.bnd.header.Attrs;
import aQute.bnd.header.OSGiHeader;
import aQute.bnd.header.Parameters;
import aQute.bnd.osgi.Analyzer;
import aQute.bnd.osgi.Clazz;
import aQute.bnd.osgi.Constants;
import aQute.bnd.osgi.Descriptors.TypeRef;
import aQute.bnd.osgi.Instruction;
import aQute.bnd.osgi.Instructions;
import aQute.bnd.service.AnalyzerPlugin;
import aQute.bnd.xmlattribute.XMLAttributeFinder;
import aQute.libg.generics.Create;
/**
* Analyze the class space for any classes that have an OSGi annotation for DS.
*/
public class MetatypeAnnotations implements AnalyzerPlugin {
enum Options {
nested, version {
@Override
void process(MetatypeAnnotations anno, Attrs attrs) {
String v = attrs.get("minimum");
if (v != null && v.length() > 0) {
anno.minVersion = MetatypeVersion.valueFor(v);
}
}
@Override
void reset(MetatypeAnnotations anno) {
anno.minVersion = MetatypeVersion.VERSION_1_2;
}
};
void process(MetatypeAnnotations anno, Attrs attrs) {
}
void reset(MetatypeAnnotations anno) {
}
static void parseOption(Map.Entry<String,Attrs> entry, EnumSet<Options> options, MetatypeAnnotations state) {
String s = entry.getKey();
boolean negation = false;
if (s.startsWith("!")) {
negation = true;
s = s.substring(1);
}
Options option = Options.valueOf(s);
if (negation) {
options.remove(option);
option.reset(state);
} else {
options.add(option);
Attrs attrs;
if ((attrs = entry.getValue()) != null) {
option.process(state, attrs);
}
}
}
};
MetatypeVersion minVersion;
public boolean analyzeJar(Analyzer analyzer) throws Exception {
this.minVersion = MetatypeVersion.VERSION_1_2;
Parameters header = OSGiHeader.parseHeader(analyzer.getProperty(Constants.METATYPE_ANNOTATIONS, "*"));
if (header.size() == 0)
return false;
Parameters optionsHeader = OSGiHeader.parseHeader(analyzer.getProperty(Constants.METATYPE_ANNOTATIONS_OPTIONS));
EnumSet<Options> options = EnumSet.noneOf(Options.class);
for (Map.Entry<String,Attrs> entry : optionsHeader.entrySet()) {
try {
Options.parseOption(entry, options, this);
} catch (IllegalArgumentException e) {
analyzer.error("Unrecognized %s value %s with attributes %s, expected values are %s",
Constants.METATYPE_ANNOTATIONS_OPTIONS, entry.getKey(), entry.getValue(),
EnumSet.allOf(Options.class));
}
}
Map<TypeRef,OCDDef> classToOCDMap = new HashMap<TypeRef,OCDDef>();
Set<String> ocdIds = new HashSet<String>();
Set<String> pids = new HashSet<String>();
Instructions instructions = new Instructions(header);
XMLAttributeFinder finder = new XMLAttributeFinder(analyzer);
List<Clazz> list = Create.list();
for (Clazz c : analyzer.getClassspace().values()) {
for (Instruction instruction : instructions.keySet()) {
if (instruction.matches(c.getFQN())) {
if (!instruction.isNegated()) {
list.add(c);
OCDDef definition = OCDReader.getOCDDef(c, analyzer, options, finder, minVersion);
if (definition != null) {
classToOCDMap.put(c.getClassName(), definition);
}
}
break;
}
}
}
// process Designate annotations after OCD annotations
for (Clazz c : list) {
DesignateReader.getDesignate(c, analyzer, classToOCDMap, finder);
}
for (Map.Entry<TypeRef,OCDDef> entry : classToOCDMap.entrySet()) {
TypeRef c = entry.getKey();
OCDDef definition = entry.getValue();
definition.prepare(analyzer);
if (!ocdIds.add(definition.id)) {
analyzer.error("Duplicate OCD id %s from class %s; known ids %s", definition.id, c.getFQN(), ocdIds);
}
for (DesignateDef dDef : definition.designates) {
if (dDef.pid != null && !pids.add(dDef.pid)) {
analyzer.error("Duplicate pid %s from class %s", dDef.pid, c.getFQN());
}
}
String name = "OSGI-INF/metatype/" + analyzer.validResourcePath(definition.id, "Invalid resource name")
+ ".xml";
analyzer.getJar().putResource(name, new TagResource(definition.getTag()));
}
return false;
}
@Override
public String toString() {
return "MetatypeAnnotations";
}
}