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 java.util.*; 022 023 import org.apache.felix.framework.util.Util; 024 import org.apache.felix.moduleloader.ICapability; 025 import org.apache.felix.moduleloader.IModule; 026 import org.osgi.framework.Constants; 027 import org.osgi.framework.Version; 028 029 public class Capability implements ICapability, Comparable 030 { 031 private final IModule m_module; 032 private final String m_namespace; 033 private final R4Directive[] m_directives; 034 private final R4Attribute[] m_attributes; 035 private final String[] m_uses; 036 private final String[][] m_includeFilter; 037 private final String[][] m_excludeFilter; 038 private volatile Map m_attrMap; 039 040 // Cached properties for performance reasons. 041 private final String m_pkgName; 042 private final Version m_pkgVersion; 043 044 public Capability(IModule module, String namespace, R4Directive[] dirs, R4Attribute[] attrs) 045 { 046 m_module = module; 047 m_namespace = namespace; 048 m_directives = dirs; 049 m_attributes = attrs; 050 051 // Find all export directives: uses, mandatory, include, and exclude. 052 String mandatory = ""; 053 String[] uses = new String[0]; 054 String[][] includeFilter = null, excludeFilter = null; 055 for (int dirIdx = 0; (m_directives != null) && (dirIdx < m_directives.length); dirIdx++) 056 { 057 if (m_directives[dirIdx].getName().equals(Constants.USES_DIRECTIVE)) 058 { 059 // Parse these uses directive. 060 StringTokenizer tok = new StringTokenizer(m_directives[dirIdx].getValue(), ","); 061 uses = new String[tok.countTokens()]; 062 for (int i = 0; i < uses.length; i++) 063 { 064 uses[i] = tok.nextToken().trim(); 065 } 066 } 067 else if (m_directives[dirIdx].getName().equals(Constants.MANDATORY_DIRECTIVE)) 068 { 069 mandatory = m_directives[dirIdx].getValue(); 070 } 071 else if (m_directives[dirIdx].getName().equals(Constants.INCLUDE_DIRECTIVE)) 072 { 073 String[] ss = ManifestParser.parseDelimitedString(m_directives[dirIdx].getValue(), ","); 074 includeFilter = new String[ss.length][]; 075 for (int filterIdx = 0; filterIdx < ss.length; filterIdx++) 076 { 077 includeFilter[filterIdx] = Util.parseSubstring(ss[filterIdx]); 078 } 079 } 080 else if (m_directives[dirIdx].getName().equals(Constants.EXCLUDE_DIRECTIVE)) 081 { 082 String[] ss = ManifestParser.parseDelimitedString(m_directives[dirIdx].getValue(), ","); 083 excludeFilter = new String[ss.length][]; 084 for (int filterIdx = 0; filterIdx < ss.length; filterIdx++) 085 { 086 excludeFilter[filterIdx] = Util.parseSubstring(ss[filterIdx]); 087 } 088 } 089 } 090 091 // Set final values. 092 m_uses = uses; 093 m_includeFilter = includeFilter; 094 m_excludeFilter = excludeFilter; 095 096 // Parse mandatory directive and mark specified 097 // attributes as mandatory. 098 StringTokenizer tok = new StringTokenizer(mandatory, ", "); 099 while (tok.hasMoreTokens()) 100 { 101 // Get attribute name. 102 String attrName = tok.nextToken().trim(); 103 // Find attribute and mark it as mandatory. 104 boolean found = false; 105 for (int i = 0; (!found) && (i < m_attributes.length); i++) 106 { 107 if (m_attributes[i].getName().equals(attrName)) 108 { 109 m_attributes[i] = new R4Attribute( 110 m_attributes[i].getName(), 111 m_attributes[i].getValue(), true); 112 found = true; 113 } 114 } 115 // If a specified mandatory attribute was not found, 116 // then error. 117 if (!found) 118 { 119 throw new IllegalArgumentException( 120 "Mandatory attribute '" + attrName + "' does not exist."); 121 } 122 } 123 124 // For performance reasons, find the package name and version properties. 125 String pkgName = null; 126 Version pkgVersion = Version.emptyVersion; 127 for (int i = 0; i < m_attributes.length; i++) 128 { 129 if (m_attributes[i].getName().equals(ICapability.PACKAGE_PROPERTY)) 130 { 131 pkgName = (String) m_attributes[i].getValue(); 132 } 133 else if (m_attributes[i].getName().equals(ICapability.VERSION_PROPERTY)) 134 { 135 pkgVersion = (Version) m_attributes[i].getValue(); 136 } 137 } 138 139 // Set final values. 140 m_pkgName = pkgName; 141 m_pkgVersion = pkgVersion; 142 } 143 144 public IModule getModule() 145 { 146 return m_module; 147 } 148 149 public String getNamespace() 150 { 151 return m_namespace; 152 } 153 154 // TODO: RB - Determine how to eliminate these non-generic methods; 155 // at least make sure they are not used in the generic resolver. 156 public String getPackageName() 157 { 158 return m_pkgName; 159 } 160 161 public Version getPackageVersion() 162 { 163 return m_pkgVersion; 164 } 165 166 public R4Directive[] getDirectives() 167 { 168 // TODO: RB - We should return copies of the arrays probably. 169 return m_directives; 170 } 171 172 public R4Attribute[] getAttributes() 173 { 174 // TODO: RB - We should return copies of the arrays probably. 175 return m_attributes; 176 } 177 178 public String[] getUses() 179 { 180 // TODO: RB - We should return copies of the arrays probably. 181 return m_uses; 182 } 183 184 public boolean isIncluded(String name) 185 { 186 if ((m_includeFilter == null) && (m_excludeFilter == null)) 187 { 188 return true; 189 } 190 191 // Get the class name portion of the target class. 192 String className = Util.getClassName(name); 193 194 // If there are no include filters then all classes are included 195 // by default, otherwise try to find one match. 196 boolean included = (m_includeFilter == null); 197 for (int i = 0; 198 (!included) && (m_includeFilter != null) && (i < m_includeFilter.length); 199 i++) 200 { 201 included = Util.checkSubstring(m_includeFilter[i], className); 202 } 203 204 // If there are no exclude filters then no classes are excluded 205 // by default, otherwise try to find one match. 206 boolean excluded = false; 207 for (int i = 0; 208 (!excluded) && (m_excludeFilter != null) && (i < m_excludeFilter.length); 209 i++) 210 { 211 excluded = Util.checkSubstring(m_excludeFilter[i], className); 212 } 213 return included && !excluded; 214 } 215 216 // TODO: RB - Terminology mismatch property vs. attribute. 217 public Map getProperties() 218 { 219 if (m_attrMap == null) 220 { 221 m_attrMap = new Map() { 222 223 public int size() 224 { 225 // A name and version attribute is always present, since it has a 226 // default value. 227 return m_attributes.length + 2; 228 } 229 230 public boolean isEmpty() 231 { 232 // A version attribute is always present, since it has a 233 // default value. 234 return false; 235 } 236 237 public boolean containsKey(Object key) 238 { 239 return (get(key) != null); 240 } 241 242 public boolean containsValue(Object value) 243 { 244 // Check the package name. 245 if (m_pkgName.equals(value)) 246 { 247 return true; 248 } 249 250 // Check the package version. 251 if (m_pkgVersion.equals(value)) 252 { 253 return true; 254 } 255 256 // Check all attributes. 257 for (int i = 0; i < m_attributes.length; i++) 258 { 259 if (m_attributes[i].getValue().equals(value)) 260 { 261 return true; 262 } 263 } 264 265 return false; 266 } 267 268 public Object get(Object key) 269 { 270 if (ICapability.PACKAGE_PROPERTY.equals(key)) 271 { 272 return m_pkgName; 273 } 274 else if (ICapability.VERSION_PROPERTY.equals(key)) 275 { 276 return m_pkgVersion; 277 } 278 279 for (int i = 0; i < m_attributes.length; i++) 280 { 281 if (m_attributes[i].getName().equals(key)) 282 { 283 return m_attributes[i].getValue(); 284 } 285 } 286 287 return null; 288 } 289 290 public Object put(Object key, Object value) 291 { 292 throw new UnsupportedOperationException("Map.put() not implemented."); 293 } 294 295 public Object remove(Object key) 296 { 297 throw new UnsupportedOperationException("Map.remove() not implemented."); 298 } 299 300 public void putAll(Map t) 301 { 302 throw new UnsupportedOperationException("Map.putAll() not implemented."); 303 } 304 305 public void clear() 306 { 307 throw new UnsupportedOperationException("Map.clear() not implemented."); 308 } 309 310 public Set keySet() 311 { 312 Set set = new HashSet(); 313 set.add(ICapability.PACKAGE_PROPERTY); 314 set.add(ICapability.VERSION_PROPERTY); 315 for (int i = 0; i < m_attributes.length; i++) 316 { 317 set.add(m_attributes[i].getName()); 318 } 319 return set; 320 } 321 322 public Collection values() 323 { 324 throw new UnsupportedOperationException("Map.values() not implemented."); 325 } 326 327 public Set entrySet() 328 { 329 throw new UnsupportedOperationException("Map.entrySet() not implemented."); 330 } 331 }; 332 } 333 return m_attrMap; 334 } 335 336 public int compareTo(Object o) 337 { 338 Capability cap = (Capability) o; 339 Version thisVersion = null; 340 Version version = null; 341 if (getNamespace().equals(ICapability.PACKAGE_NAMESPACE)) 342 { 343 thisVersion = getPackageVersion(); 344 version = cap.getPackageVersion(); 345 } 346 else if (getNamespace().equals(ICapability.MODULE_NAMESPACE)) 347 { 348 thisVersion = (Version) getProperties().get(Constants.BUNDLE_VERSION_ATTRIBUTE); 349 version = (Version) cap.getProperties().get(Constants.BUNDLE_VERSION_ATTRIBUTE); 350 } 351 if ((thisVersion != null) && (version != null)) 352 { 353 int cmp = thisVersion.compareTo(version); 354 if (cmp < 0) 355 { 356 return 1; 357 } 358 else if (cmp > 0) 359 { 360 return -1; 361 } 362 else 363 { 364 long thisId = m_module.getBundle().getBundleId(); 365 long id = cap.getModule().getBundle().getBundleId(); 366 if (thisId < id) 367 { 368 return -1; 369 } 370 else if (thisId > id) 371 { 372 return 1; 373 } 374 return 0; 375 } 376 } 377 else 378 { 379 return -1; 380 } 381 } 382 383 public String toString() 384 { 385 StringBuffer sb = new StringBuffer(); 386 sb.append(getNamespace()); 387 for (int i = 0; (m_directives != null) && (i < m_directives.length); i++) 388 { 389 sb.append(";"); 390 sb.append(m_directives[i].getName()); 391 sb.append(":=\""); 392 sb.append(m_directives[i].getValue()); 393 sb.append("\""); 394 } 395 for (int i = 0; (m_attributes != null) && (i < m_attributes.length); i++) 396 { 397 sb.append(";"); 398 sb.append(m_attributes[i].getName()); 399 sb.append("=\""); 400 sb.append(m_attributes[i].getValue()); 401 sb.append("\""); 402 } 403 return sb.toString(); 404 } 405 }