001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 package org.apache.felix.framework.util.manifestparser; 020 021 import org.apache.felix.framework.util.MapToDictionary; 022 import org.apache.felix.framework.util.VersionRange; 023 import org.apache.felix.moduleloader.ICapability; 024 import org.apache.felix.moduleloader.IRequirement; 025 import org.osgi.framework.*; 026 027 public class Requirement implements IRequirement 028 { 029 private final String m_namespace; 030 private final R4Directive[] m_directives; 031 private final R4Attribute[] m_attributes; 032 private final boolean m_isOptional; 033 034 private final String m_targetName; 035 private final VersionRange m_targetVersionRange; 036 private volatile Filter m_filter; 037 038 public Requirement(String namespace, String filterStr) throws InvalidSyntaxException 039 { 040 m_namespace = namespace; 041 m_filter = FrameworkUtil.createFilter(filterStr); 042 m_directives = null; 043 m_attributes = null; 044 m_isOptional = false; 045 m_targetName = null; 046 m_targetVersionRange = null; 047 } 048 049 public Requirement(String namespace, R4Directive[] directives, R4Attribute[] attributes) 050 { 051 m_namespace = namespace; 052 m_directives = directives; 053 m_attributes = attributes; 054 m_filter = null; 055 056 // Find all import directives: resolution. 057 boolean optional = false; 058 for (int i = 0; (m_directives != null) && (i < m_directives.length); i++) 059 { 060 if (m_directives[i].getName().equals(Constants.RESOLUTION_DIRECTIVE)) 061 { 062 optional = m_directives[i].getValue().equals(Constants.RESOLUTION_OPTIONAL); 063 } 064 } 065 m_isOptional = optional; 066 067 String targetName = null; 068 VersionRange targetVersionRange = VersionRange.infiniteRange; 069 for (int i = 0; i < m_attributes.length; i++) 070 { 071 if (m_namespace.equals(ICapability.MODULE_NAMESPACE)) 072 { 073 if (m_attributes[i].getName().equals(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE)) 074 { 075 targetName = (String) m_attributes[i].getValue(); 076 } 077 else if (m_attributes[i].getName().equals(Constants.BUNDLE_VERSION_ATTRIBUTE)) 078 { 079 targetVersionRange = (VersionRange) m_attributes[i].getValue(); 080 } 081 } 082 else if (m_namespace.equals(ICapability.PACKAGE_NAMESPACE)) 083 { 084 if (m_attributes[i].getName().equals(ICapability.PACKAGE_PROPERTY)) 085 { 086 targetName = (String) m_attributes[i].getValue(); 087 } 088 else if (m_attributes[i].getName().equals(ICapability.VERSION_PROPERTY)) 089 { 090 targetVersionRange = (VersionRange) m_attributes[i].getValue(); 091 } 092 } 093 else if (m_namespace.equals(ICapability.HOST_NAMESPACE)) 094 { 095 if (m_attributes[i].getName().equals(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE)) 096 { 097 targetName = (String) m_attributes[i].getValue(); 098 } 099 else if (m_attributes[i].getName().equals(Constants.BUNDLE_VERSION_ATTRIBUTE)) 100 { 101 targetVersionRange = (VersionRange) m_attributes[i].getValue(); 102 } 103 } 104 } 105 m_targetName = targetName; 106 m_targetVersionRange = targetVersionRange; 107 } 108 109 public String getNamespace() 110 { 111 return m_namespace; 112 } 113 114 public Filter getFilter() 115 { 116 if (m_filter == null) 117 { 118 m_filter = convertToFilter(); 119 } 120 return m_filter; 121 } 122 123 // TODO: RB - We need to verify that the resolver code does not 124 // touch these implementation-specific methods. 125 126 public String getTargetName() 127 { 128 return m_targetName; 129 } 130 131 public VersionRange getTargetVersionRange() 132 { 133 return m_targetVersionRange; 134 } 135 136 public R4Directive[] getDirectives() 137 { 138 // TODO: RB - We should return copies of the arrays probably. 139 return m_directives; 140 } 141 142 public R4Attribute[] getAttributes() 143 { 144 // TODO: RB - We should return copies of the arrays probably. 145 return m_attributes; 146 } 147 148 public boolean isMultiple() 149 { 150 return false; 151 } 152 153 public boolean isOptional() 154 { 155 return m_isOptional; 156 } 157 158 public String getComment() 159 { 160 return "Comment for " + toString(); 161 } 162 163 public boolean isSatisfied(ICapability capability) 164 { 165 // If the requirement was constructed with a filter, then 166 // we must use that filter for evaluation. 167 if ((m_attributes == null) && (m_filter != null)) 168 { 169 return m_namespace.equals(capability.getNamespace()) && 170 getFilter().match(new MapToDictionary(capability.getProperties())); 171 } 172 // Otherwise, if the requirement was constructed with attributes, then 173 // perform the evaluation manually instead of using the filter for 174 // performance reasons. 175 else if (m_attributes != null) 176 { 177 return capability.getNamespace().equals(getNamespace()) && 178 doAttributesMatch((Capability) capability); 179 } 180 181 return false; 182 } 183 184 private boolean doAttributesMatch(Capability ec) 185 { 186 // Grab the capability's attributes. 187 R4Attribute[] capAttrs = ec.getAttributes(); 188 189 // Cycle through all attributes of this import package 190 // and make sure its values match the attribute values 191 // of the specified export package. 192 for (int reqAttrIdx = 0; reqAttrIdx < m_attributes.length; reqAttrIdx++) 193 { 194 // Get current attribute from this import package. 195 R4Attribute reqAttr = m_attributes[reqAttrIdx]; 196 197 // Check if the export package has the same attribute. 198 boolean found = false; 199 for (int capAttrIdx = 0; 200 (!found) && (capAttrIdx < capAttrs.length); 201 capAttrIdx++) 202 { 203 // Get current attribute for the export package. 204 R4Attribute capAttr = capAttrs[capAttrIdx]; 205 // Check if the attribute names are equal. 206 if (reqAttr.getName().equals(capAttr.getName())) 207 { 208 // We only recognize version types. If the value of the 209 // attribute is a version/version range, then we use the 210 // "in range" comparison, otherwise we simply use equals(). 211 if (capAttr.getValue() instanceof Version) 212 { 213 if (!((VersionRange) reqAttr.getValue()).isInRange((Version) capAttr.getValue())) 214 { 215 return false; 216 } 217 } 218 else if (capAttr.getValue() instanceof Object[]) 219 { 220 Object[] values = (Object[]) capAttr.getValue(); 221 boolean matched = false; 222 for (int valIdx = 0; !matched && (valIdx < values.length); valIdx++) 223 { 224 if (reqAttr.getValue().equals(values[valIdx])) 225 { 226 matched = true; 227 } 228 } 229 if (!matched) 230 { 231 return false; 232 } 233 } 234 else if (!reqAttr.getValue().equals(capAttr.getValue())) 235 { 236 return false; 237 } 238 found = true; 239 } 240 } 241 // If the attribute was not found, then return false. 242 if (!found) 243 { 244 return false; 245 } 246 } 247 248 // Now, cycle through all attributes of the export package and verify that 249 // all mandatory attributes are present in this import package. 250 for (int capAttrIdx = 0; capAttrIdx < capAttrs.length; capAttrIdx++) 251 { 252 // Get current attribute for this package. 253 R4Attribute capAttr = capAttrs[capAttrIdx]; 254 255 // If the export attribute is mandatory, then make sure 256 // this import package has the attribute. 257 if (capAttr.isMandatory()) 258 { 259 boolean found = false; 260 for (int reqAttrIdx = 0; 261 (!found) && (reqAttrIdx < m_attributes.length); 262 reqAttrIdx++) 263 { 264 // Get current attribute from specified package. 265 R4Attribute reqAttr = m_attributes[reqAttrIdx]; 266 267 // Check if the attribute names are equal 268 // and set found flag. 269 if (capAttr.getName().equals(reqAttr.getName())) 270 { 271 found = true; 272 } 273 } 274 // If not found, then return false. 275 if (!found) 276 { 277 return false; 278 } 279 } 280 } 281 282 return true; 283 } 284 285 private Filter convertToFilter() 286 { 287 StringBuffer sb = new StringBuffer(); 288 if ((m_attributes != null) && (m_attributes.length > 1)) 289 { 290 sb.append("(&"); 291 } 292 for (int i = 0; (m_attributes != null) && (i < m_attributes.length); i++) 293 { 294 // If this is a package import, then convert wild-carded 295 // dynamically imported package names to an OR comparison. 296 if (m_namespace.equals(ICapability.PACKAGE_NAMESPACE) && 297 m_attributes[i].getName().equals(ICapability.PACKAGE_PROPERTY) && 298 m_attributes[i].getValue().toString().endsWith(".*")) 299 { 300 int idx = m_attributes[i].getValue().toString().indexOf(".*"); 301 sb.append("(|(package="); 302 sb.append(m_attributes[i].getValue().toString().substring(0, idx)); 303 sb.append(")(package="); 304 sb.append(m_attributes[i].getValue().toString()); 305 sb.append("))"); 306 } 307 else if (m_attributes[i].getValue() instanceof VersionRange) 308 { 309 VersionRange vr = (VersionRange) m_attributes[i].getValue(); 310 if (vr.isLowInclusive()) 311 { 312 sb.append("("); 313 sb.append(m_attributes[i].getName()); 314 sb.append(">="); 315 sb.append(vr.getLow().toString()); 316 sb.append(")"); 317 } 318 else 319 { 320 sb.append("(!("); 321 sb.append(m_attributes[i].getName()); 322 sb.append("<="); 323 sb.append(vr.getLow().toString()); 324 sb.append("))"); 325 } 326 327 if (vr.getHigh() != null) 328 { 329 if (vr.isHighInclusive()) 330 { 331 sb.append("("); 332 sb.append(m_attributes[i].getName()); 333 sb.append("<="); 334 sb.append(vr.getHigh().toString()); 335 sb.append(")"); 336 } 337 else 338 { 339 sb.append("(!("); 340 sb.append(m_attributes[i].getName()); 341 sb.append(">="); 342 sb.append(vr.getHigh().toString()); 343 sb.append("))"); 344 } 345 } 346 } 347 else 348 { 349 sb.append("("); 350 sb.append(m_attributes[i].getName()); 351 sb.append("="); 352 sb.append(m_attributes[i].getValue().toString()); 353 sb.append(")"); 354 } 355 } 356 357 if ((m_attributes != null) && (m_attributes.length > 1)) 358 { 359 sb.append(")"); 360 } 361 362 try 363 { 364 return FrameworkUtil.createFilter(sb.toString()); 365 } 366 catch (InvalidSyntaxException ex) 367 { 368 // This should never happen, so we can safely ignore. 369 } 370 371 return null; 372 } 373 374 public String toString() 375 { 376 return getNamespace() + "; " + getFilter().toString(); 377 } 378 }