package aQute.bnd.component; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.osgi.service.component.annotations.ConfigurationPolicy; import org.osgi.service.component.annotations.ServiceScope; import aQute.bnd.osgi.Analyzer; import aQute.bnd.osgi.Descriptors.TypeRef; import aQute.bnd.version.Version; import aQute.bnd.xmlattribute.ExtensionDef; import aQute.bnd.xmlattribute.Namespaces; import aQute.bnd.xmlattribute.XMLAttributeFinder; import aQute.lib.collections.MultiMap; import aQute.lib.tag.Tag; /** * This class just holds the information for the component, implementation, and * service/provide elements. The {@link #prepare(Analyzer)} method will check if * things are ok and the {@link #getTag()} method returns a tag if the prepare * method returns without any errors. The class uses {@link ReferenceDef} to * hold the references. */ class ComponentDef extends ExtensionDef { final static String NAMESPACE_STEM = "http://www.osgi.org/xmlns/scr"; final static String MARKER = new String("|marker"); final List<String> properties = new ArrayList<String>(); final MultiMap<String,String> property = new MultiMap<String,String>(); // key // is // property // name final Map<String,String> propertyType = new HashMap<String,String>(); final Map<String,ReferenceDef> references = new LinkedHashMap<String,ReferenceDef>(); Version version; String name; String factory; Boolean immediate; ServiceScope scope; ConfigurationPolicy configurationPolicy; TypeRef implementation; TypeRef service[]; String activate; String deactivate; String modified; Boolean enabled; String xmlns; String[] configurationPid; List<Tag> propertyTags = new ArrayList<Tag>(); public ComponentDef(XMLAttributeFinder finder, Version minVersion) { super(finder); version = minVersion; } String effectiveName() { if (name != null) return name; if (implementation != null) return implementation.getFQN(); return "<name not yet determined>"; } /** * Called to prepare. If will look for any errors or inconsistencies in the * setup. * * @param analyzer the analyzer to report errors and create references * @throws Exception */ void prepare(Analyzer analyzer) throws Exception { prepareVersion(analyzer); if (implementation == null) { analyzer.error("No Implementation defined for component %s", name); return; } analyzer.referTo(implementation); if (name == null) name = implementation.getFQN(); if (service != null && service.length > 0) { for (TypeRef interfaceName : service) analyzer.referTo(interfaceName); } else if (scope != null && scope != ServiceScope.BUNDLE) analyzer.warning("The servicefactory:=true directive is set but no service is provided, ignoring it"); for (Map.Entry<String,List<String>> kvs : property.entrySet()) { Tag property = new Tag("property"); String name = kvs.getKey(); String type = propertyType.get(name); property.addAttribute("name", name); if (type != null) { property.addAttribute("type", type); } if (kvs.getValue().size() == 1) { String value = kvs.getValue().get(0); value = check(type, value, analyzer); property.addAttribute("value", value); } else { StringBuilder sb = new StringBuilder(); String del = ""; for (String v : kvs.getValue()) { if (v == MARKER) { continue; } sb.append(del); v = check(type, v, analyzer); sb.append(v); del = "\n"; } property.addContent(sb.toString()); } propertyTags.add(property); } } private void prepareVersion(Analyzer analyzer) throws Exception { for (ReferenceDef ref : references.values()) { ref.prepare(analyzer); updateVersion(ref.version); } if (configurationPolicy != null) updateVersion(AnnotationReader.V1_1); if (configurationPid != null) updateVersion(AnnotationReader.V1_2); if (modified != null) updateVersion(AnnotationReader.V1_1); } void sortReferences() { Map<String,ReferenceDef> temp = new TreeMap<String,ReferenceDef>(references); references.clear(); references.putAll(temp); } /** * Returns a tag describing the component element. * * @return a component element */ Tag getTag() { String xmlns = this.xmlns; if (xmlns == null && !version.equals(AnnotationReader.V1_0)) xmlns = NAMESPACE_STEM + "/v" + version; Tag component = new Tag(xmlns == null ? "component" : "scr:component"); Namespaces namespaces = null; if (xmlns != null) { namespaces = new Namespaces(); namespaces.registerNamespace("scr", xmlns); addNamespaces(namespaces, xmlns); for (ReferenceDef ref : references.values()) ref.addNamespaces(namespaces, xmlns); namespaces.addNamespaces(component); } component.addAttribute("name", name); if (configurationPolicy != null) component.addAttribute("configuration-policy", configurationPolicy.toString().toLowerCase()); if (enabled != null) component.addAttribute("enabled", enabled); if (immediate != null) component.addAttribute("immediate", immediate); if (factory != null) component.addAttribute("factory", factory); if (activate != null && !version.equals(AnnotationReader.V1_0)) component.addAttribute("activate", activate); if (deactivate != null && !version.equals(AnnotationReader.V1_0)) component.addAttribute("deactivate", deactivate); if (modified != null) component.addAttribute("modified", modified); if (configurationPid != null) { StringBuilder b = new StringBuilder(); String space = ""; for (String pid : configurationPid) { if ("$".equals(pid)) pid = name; b.append(space).append(pid); space = " "; } component.addAttribute("configuration-pid", b.toString()); } addAttributes(component, namespaces); Tag impl = new Tag(component, "implementation"); impl.addAttribute("class", implementation.getFQN()); if (service != null && service.length != 0) { Tag s = new Tag(component, "service"); if (scope != null) {// TODO check for DEFAULT??? if (AnnotationReader.V1_3.compareTo(version) > 0) { if (scope == ServiceScope.PROTOTYPE) { throw new IllegalStateException("verification failed, pre 1.3 component with scope PROTOTYPE"); } s.addAttribute("servicefactory", scope == ServiceScope.BUNDLE); } else { s.addAttribute("scope", scope.toString().toLowerCase()); } } for (TypeRef ss : service) { Tag provide = new Tag(s, "provide"); provide.addAttribute("interface", ss.getFQN()); } } for (ReferenceDef ref : references.values()) { Tag refTag = ref.getTag(namespaces); component.addContent(refTag); } for (Tag tag : propertyTags) component.addContent(tag); for (String entry : properties) { Tag properties = new Tag(component, "properties"); properties.addAttribute("entry", entry); } return component; } private String check(String type, String v, Analyzer analyzer) { if (type == null) return v; try { if (type.equals("Char")) type = "Character"; Class< ? > c = Class.forName("java.lang." + type); if (c == String.class) return v; v = v.trim(); if (c == Character.class) c = Integer.class; Method m = c.getMethod("valueOf", String.class); m.invoke(null, v); } catch (ClassNotFoundException e) { analyzer.error("Invalid data type %s", type); } catch (NoSuchMethodException e) { analyzer.error("Cannot convert data %s to type %s", v, type); } catch (NumberFormatException e) { analyzer.error("Not a valid number %s for %s, %s", v, type, e.getMessage()); } catch (Exception e) { analyzer.error("Cannot convert data %s to type %s", v, type); } return v; } void updateVersion(Version version) { this.version = max(this.version, version); } static <T extends Comparable<T>> T max(T a, T b) { int n = a.compareTo(b); if (n >= 0) return a; return b; } }