Stuart McCulloch | bb01437 | 2012-06-07 21:57:32 +0000 | [diff] [blame] | 1 | package aQute.bnd.component; |
| 2 | |
| 3 | import java.lang.reflect.*; |
| 4 | import java.util.*; |
| 5 | |
| 6 | import org.osgi.service.component.annotations.*; |
| 7 | |
| 8 | import aQute.lib.collections.*; |
| 9 | import aQute.lib.osgi.*; |
| 10 | import aQute.lib.osgi.Descriptors.TypeRef; |
| 11 | import aQute.lib.tag.*; |
| 12 | import aQute.libg.version.*; |
| 13 | |
| 14 | /** |
| 15 | * This class just holds the information for the component, implementation, and |
| 16 | * service/provide elements. The {@link #prepare(Analyzer)} method will check if |
| 17 | * things are ok and the {@link #getTag()} method returns a tag if the prepare |
| 18 | * method returns without any errors. The class uses {@link ReferenceDef} to |
| 19 | * hold the references. |
| 20 | */ |
| 21 | class ComponentDef { |
| 22 | final static String NAMESPACE_STEM = "http://www.osgi.org/xmlns/scr"; |
| 23 | final List<String> properties = new ArrayList<String>(); |
| 24 | final MultiMap<String, String> property = new MultiMap<String, String>(); |
| 25 | final Map<String, ReferenceDef> references = new TreeMap<String, ReferenceDef>(); |
| 26 | |
| 27 | Version version = AnnotationReader.V1_1; |
| 28 | String name; |
| 29 | String factory; |
| 30 | Boolean immediate; |
| 31 | Boolean servicefactory; |
| 32 | ConfigurationPolicy configurationPolicy; |
| 33 | TypeRef implementation; |
| 34 | TypeRef service[]; |
| 35 | String activate; |
| 36 | String deactivate; |
| 37 | String modified; |
| 38 | Boolean enabled; |
| 39 | String xmlns; |
| 40 | String configurationPid; |
| 41 | List<Tag> propertyTags = new ArrayList<Tag>(); |
| 42 | |
| 43 | /** |
| 44 | * Called to prepare. If will look for any errors or inconsistencies in the |
| 45 | * setup. |
| 46 | * |
| 47 | * @param analyzer |
| 48 | * the analyzer to report errors and create references |
| 49 | * @throws Exception |
| 50 | */ |
| 51 | void prepare(Analyzer analyzer) throws Exception { |
| 52 | |
| 53 | for (ReferenceDef ref : references.values()) { |
| 54 | ref.prepare(analyzer); |
| 55 | if (ref.version.compareTo(version) > 0) |
| 56 | version = ref.version; |
| 57 | } |
| 58 | |
| 59 | if (implementation == null) { |
| 60 | analyzer.error("No Implementation defined for component " + name); |
| 61 | return; |
| 62 | } |
| 63 | |
| 64 | analyzer.referTo(implementation); |
| 65 | |
| 66 | if (name == null) |
| 67 | name = implementation.getFQN(); |
| 68 | |
| 69 | if (service != null && service.length > 0) { |
| 70 | for (TypeRef interfaceName : service) |
| 71 | analyzer.referTo(interfaceName); |
| 72 | } else if (servicefactory != null && servicefactory) |
| 73 | analyzer.warning("The servicefactory:=true directive is set but no service is provided, ignoring it"); |
| 74 | |
| 75 | if (configurationPid != null) |
| 76 | version = ReferenceDef.max(version, AnnotationReader.V1_2); |
| 77 | |
| 78 | for (Map.Entry<String, List<String>> kvs : property.entrySet()) { |
| 79 | Tag property = new Tag("property"); |
| 80 | String name = kvs.getKey(); |
| 81 | String type = null; |
| 82 | int n = name.indexOf(':'); |
| 83 | if (n > 0) { |
| 84 | type = name.substring(n + 1); |
| 85 | name = name.substring(0, n); |
| 86 | } |
| 87 | |
| 88 | property.addAttribute("name", name); |
| 89 | if (type != null) { |
| 90 | property.addAttribute("type", type); |
| 91 | } |
| 92 | if (kvs.getValue().size() == 1) { |
| 93 | String value = kvs.getValue().get(0); |
| 94 | value = check(type, value, analyzer); |
| 95 | property.addAttribute("value", value); |
| 96 | } else { |
| 97 | StringBuilder sb = new StringBuilder(); |
| 98 | |
| 99 | String del = ""; |
| 100 | for (String v : kvs.getValue()) { |
| 101 | sb.append(del); |
| 102 | v = check(type, v, analyzer); |
| 103 | sb.append(v); |
| 104 | del = "\n"; |
| 105 | } |
| 106 | property.addContent(sb.toString()); |
| 107 | } |
| 108 | propertyTags.add(property); |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | /** |
| 113 | * Returns a tag describing the component element. |
| 114 | * |
| 115 | * @return a component element |
| 116 | */ |
| 117 | Tag getTag() { |
| 118 | Tag component = new Tag("scr:component"); |
| 119 | if (xmlns != null) |
| 120 | component.addAttribute("xmlns:scr", xmlns); |
| 121 | else |
| 122 | component.addAttribute("xmlns:scr", NAMESPACE_STEM + "/v" + version); |
| 123 | |
| 124 | component.addAttribute("name", name); |
| 125 | |
| 126 | if (servicefactory != null) |
| 127 | component.addAttribute("servicefactory", servicefactory); |
| 128 | |
| 129 | if (configurationPolicy != null) |
| 130 | component.addAttribute("configuration-policy", configurationPolicy.toString() |
| 131 | .toLowerCase()); |
| 132 | |
| 133 | if (enabled != null) |
| 134 | component.addAttribute("enabled", enabled); |
| 135 | |
| 136 | if (immediate != null) |
| 137 | component.addAttribute("immediate", immediate); |
| 138 | |
| 139 | if (factory != null) |
| 140 | component.addAttribute("factory", factory); |
| 141 | |
| 142 | if (activate != null) |
| 143 | component.addAttribute("activate", activate); |
| 144 | |
| 145 | if (deactivate != null) |
| 146 | component.addAttribute("deactivate", deactivate); |
| 147 | |
| 148 | if (modified != null) |
| 149 | component.addAttribute("modified", modified); |
| 150 | |
| 151 | if (configurationPid != null) |
| 152 | component.addAttribute("configuration-pid", configurationPid); |
| 153 | |
| 154 | Tag impl = new Tag(component, "implementation"); |
| 155 | impl.addAttribute("class", implementation.getFQN()); |
| 156 | |
| 157 | if (service != null && service.length != 0) { |
| 158 | Tag s = new Tag(component, "service"); |
| 159 | if (servicefactory != null && servicefactory) |
| 160 | s.addAttribute("servicefactory", true); |
| 161 | |
| 162 | for (TypeRef ss : service) { |
| 163 | Tag provide = new Tag(s, "provide"); |
| 164 | provide.addAttribute("interface", ss.getFQN()); |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | for (ReferenceDef ref : references.values()) { |
| 169 | Tag refTag = ref.getTag(); |
| 170 | component.addContent(refTag); |
| 171 | } |
| 172 | |
| 173 | for (Tag tag : propertyTags) |
| 174 | component.addContent(tag); |
| 175 | |
| 176 | for (String entry : properties) { |
| 177 | Tag properties = new Tag(component, "properties"); |
| 178 | properties.addAttribute("entry", entry); |
| 179 | } |
| 180 | return component; |
| 181 | } |
| 182 | |
| 183 | private String check(String type, String v, Analyzer analyzer) { |
| 184 | if (type == null) |
| 185 | return v; |
| 186 | |
| 187 | try { |
| 188 | Class<?> c = Class.forName("java.lang." + type); |
| 189 | if (c == String.class) |
| 190 | return v; |
| 191 | |
| 192 | v = v.trim(); |
| 193 | if (c == Character.class) |
| 194 | c = Integer.class; |
| 195 | Method m = c.getMethod("valueOf", String.class); |
| 196 | m.invoke(null, v); |
| 197 | } catch (ClassNotFoundException e) { |
| 198 | analyzer.error("Invalid data type %s", type); |
| 199 | } catch (NoSuchMethodException e) { |
| 200 | analyzer.error("Cannot convert data %s to type %s", v, type); |
| 201 | } catch (NumberFormatException e) { |
| 202 | analyzer.error("Not a valid number %s for %s, %s", v, type, e.getMessage()); |
| 203 | } catch (Exception e) { |
| 204 | analyzer.error("Cannot convert data %s to type %s", v, type); |
| 205 | } |
| 206 | return v; |
| 207 | } |
| 208 | } |