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.Logger; 024 import org.apache.felix.framework.util.FelixConstants; 025 import org.apache.felix.framework.util.VersionRange; 026 import org.osgi.framework.*; 027 028 public class R4LibraryClause 029 { 030 private final String[] m_libraryEntries; 031 private final String[] m_osnames; 032 private final String[] m_processors; 033 private final String[] m_osversions; 034 private final String[] m_languages; 035 private final String m_selectionFilter; 036 037 public R4LibraryClause(String[] libraryEntries, String[] osnames, 038 String[] processors, String[] osversions, String[] languages, 039 String selectionFilter) 040 { 041 m_libraryEntries = libraryEntries; 042 m_osnames = osnames; 043 m_processors = processors; 044 m_osversions = osversions; 045 m_languages = languages; 046 m_selectionFilter = selectionFilter; 047 } 048 049 public R4LibraryClause(R4LibraryClause library) 050 { 051 m_libraryEntries = library.m_libraryEntries; 052 m_osnames = library.m_osnames; 053 m_osversions = library.m_osversions; 054 m_processors = library.m_processors; 055 m_languages = library.m_languages; 056 m_selectionFilter = library.m_selectionFilter; 057 } 058 059 public String[] getLibraryEntries() 060 { 061 return m_libraryEntries; 062 } 063 064 public String[] getOSNames() 065 { 066 return m_osnames; 067 } 068 069 public String[] getProcessors() 070 { 071 return m_processors; 072 } 073 074 public String[] getOSVersions() 075 { 076 return m_osversions; 077 } 078 079 public String[] getLanguages() 080 { 081 return m_languages; 082 } 083 084 public String getSelectionFilter() 085 { 086 return m_selectionFilter; 087 } 088 089 public boolean match(Map configMap) throws BundleException 090 { 091 String normal_osname = normalizeOSName((String) configMap.get(Constants.FRAMEWORK_OS_NAME)); 092 String normal_processor = normalizeProcessor((String) configMap.get(Constants.FRAMEWORK_PROCESSOR)); 093 String normal_osversion = normalizeOSVersion((String) configMap.get(Constants.FRAMEWORK_OS_VERSION)); 094 String normal_language = (String) configMap.get(Constants.FRAMEWORK_LANGUAGE); 095 096 // Check library's osname. 097 if (!checkOSNames(normal_osname, getOSNames())) 098 { 099 return false; 100 } 101 102 // Check library's processor. 103 if (!checkProcessors(normal_processor, getProcessors())) 104 { 105 return false; 106 } 107 108 // Check library's osversion if specified. 109 if ((getOSVersions() != null) && 110 (getOSVersions().length > 0) && 111 !checkOSVersions(normal_osversion, getOSVersions())) 112 { 113 return false; 114 } 115 116 // Check library's language if specified. 117 if ((getLanguages() != null) && 118 (getLanguages().length > 0) && 119 !checkLanguages(normal_language, getLanguages())) 120 { 121 return false; 122 } 123 124 // Check library's selection-filter if specified. 125 if ((getSelectionFilter() != null) && 126 (getSelectionFilter().length() >= 0) && 127 !checkSelectionFilter(configMap, getSelectionFilter())) 128 { 129 return false; 130 } 131 132 return true; 133 } 134 135 private boolean checkOSNames(String currentOSName, String[] osnames) 136 { 137 boolean win32 = currentOSName.startsWith("win") && 138 (currentOSName.equals("windows95") 139 || currentOSName.equals("windows98") 140 || currentOSName.equals("windowsnt") 141 || currentOSName.equals("windows2000") 142 || currentOSName.equals("windows2003") 143 || currentOSName.equals("windowsxp") 144 || currentOSName.equals("windowsce") 145 || currentOSName.equals("windowsvista") 146 || currentOSName.equals("windows7")); 147 148 for (int i = 0; (osnames != null) && (i < osnames.length); i++) 149 { 150 if (osnames[i].equals(currentOSName) || 151 ("win32".equals(osnames[i]) && win32)) 152 { 153 return true; 154 } 155 } 156 return false; 157 } 158 159 private boolean checkProcessors(String currentProcessor, String[] processors) 160 { 161 for (int i = 0; (processors != null) && (i < processors.length); i++) 162 { 163 if (processors[i].equals(currentProcessor)) 164 { 165 return true; 166 } 167 } 168 return false; 169 } 170 171 private boolean checkOSVersions(String currentOSVersion, String[] osversions) 172 throws BundleException 173 { 174 for (int i = 0; (osversions != null) && (i < osversions.length); i++) 175 { 176 try 177 { 178 VersionRange range = VersionRange.parse(osversions[i]); 179 if (range.isInRange(new Version(currentOSVersion))) 180 { 181 return true; 182 } 183 } 184 catch (Exception ex) 185 { 186 throw new BundleException( 187 "Error evaluating osversion: " + osversions[i], ex); 188 } 189 } 190 return false; 191 } 192 193 private boolean checkLanguages(String currentLanguage, String[] languages) 194 { 195 for (int i = 0; (languages != null) && (i < languages.length); i++) 196 { 197 if (languages[i].equals(currentLanguage)) 198 { 199 return true; 200 } 201 } 202 return false; 203 } 204 205 private boolean checkSelectionFilter(Map configMap, String expr) 206 throws BundleException 207 { 208 // Get all framework properties 209 Dictionary dict = new Hashtable(); 210 for (Iterator i = configMap.keySet().iterator(); i.hasNext(); ) 211 { 212 Object key = i.next(); 213 dict.put(key, configMap.get(key)); 214 } 215 // Compute expression 216 try 217 { 218 Filter filter = FrameworkUtil.createFilter(expr); 219 return filter.match(dict); 220 } 221 catch (Exception ex) 222 { 223 throw new BundleException( 224 "Error evaluating filter expression: " + expr, ex); 225 } 226 } 227 228 public static R4LibraryClause parse(Logger logger, String s) 229 { 230 try 231 { 232 if ((s == null) || (s.length() == 0)) 233 { 234 return null; 235 } 236 237 if (s.equals(FelixConstants.BUNDLE_NATIVECODE_OPTIONAL)) 238 { 239 return new R4LibraryClause(null, null, null, null, null, null); 240 } 241 242 // The tokens are separated by semicolons and may include 243 // any number of libraries along with one set of associated 244 // properties. 245 StringTokenizer st = new StringTokenizer(s, ";"); 246 String[] libEntries = new String[st.countTokens()]; 247 List osNameList = new ArrayList(); 248 List osVersionList = new ArrayList(); 249 List processorList = new ArrayList(); 250 List languageList = new ArrayList(); 251 String selectionFilter = null; 252 int libCount = 0; 253 while (st.hasMoreTokens()) 254 { 255 String token = st.nextToken().trim(); 256 if (token.indexOf('=') < 0) 257 { 258 // Remove the slash, if necessary. 259 libEntries[libCount] = (token.charAt(0) == '/') 260 ? token.substring(1) 261 : token; 262 libCount++; 263 } 264 else 265 { 266 // Check for valid native library properties; defined as 267 // a property name, an equal sign, and a value. 268 // NOTE: StringTokenizer can not be used here because 269 // a value can contain one or more "=" too, e.g., 270 // selection-filter="(org.osgi.framework.windowing.system=gtk)" 271 String property = null; 272 String value = null; 273 if (!(token.indexOf("=") > 1)) 274 { 275 throw new IllegalArgumentException( 276 "Bundle manifest native library entry malformed: " + token); 277 } 278 else 279 { 280 property = (token.substring(0, token.indexOf("="))) 281 .trim().toLowerCase(); 282 value = (token.substring(token.indexOf("=") + 1, token 283 .length())).trim(); 284 } 285 286 // Values may be quoted, so remove quotes if present. 287 if (value.charAt(0) == '"') 288 { 289 // This should always be true, otherwise the 290 // value wouldn't be properly quoted, but we 291 // will check for safety. 292 if (value.charAt(value.length() - 1) == '"') 293 { 294 value = value.substring(1, value.length() - 1); 295 } 296 else 297 { 298 value = value.substring(1); 299 } 300 } 301 // Add the value to its corresponding property list. 302 if (property.equals(Constants.BUNDLE_NATIVECODE_OSNAME)) 303 { 304 osNameList.add(normalizeOSName(value)); 305 } 306 else if (property.equals(Constants.BUNDLE_NATIVECODE_OSVERSION)) 307 { 308 osVersionList.add(normalizeOSVersion(value)); 309 } 310 else if (property.equals(Constants.BUNDLE_NATIVECODE_PROCESSOR)) 311 { 312 processorList.add(normalizeProcessor(value)); 313 } 314 else if (property.equals(Constants.BUNDLE_NATIVECODE_LANGUAGE)) 315 { 316 languageList.add(value); 317 } 318 else if (property.equals(Constants.SELECTION_FILTER_ATTRIBUTE)) 319 { 320 // TODO: NATIVE - I believe we can have multiple selection filters too. 321 selectionFilter = value; 322 } 323 } 324 } 325 326 if (libCount == 0) 327 { 328 return null; 329 } 330 331 // Shrink lib file array. 332 String[] actualLibEntries = new String[libCount]; 333 System.arraycopy(libEntries, 0, actualLibEntries, 0, libCount); 334 return new R4LibraryClause( 335 actualLibEntries, 336 (String[]) osNameList.toArray(new String[osNameList.size()]), 337 (String[]) processorList.toArray(new String[processorList.size()]), 338 (String[]) osVersionList.toArray(new String[osVersionList.size()]), 339 (String[]) languageList.toArray(new String[languageList.size()]), 340 selectionFilter); 341 } 342 catch (RuntimeException ex) 343 { 344 logger.log(Logger.LOG_ERROR, 345 "Error parsing native library header.", ex); 346 throw ex; 347 } 348 } 349 350 public static String normalizeOSName(String value) 351 { 352 value = value.toLowerCase(); 353 354 if (value.startsWith("win")) 355 { 356 String os = "win"; 357 if (value.indexOf("32") >= 0 || value.indexOf("*") >= 0) 358 { 359 os = "win32"; 360 } 361 else if (value.indexOf("95") >= 0) 362 { 363 os = "windows95"; 364 } 365 else if (value.indexOf("98") >= 0) 366 { 367 os = "windows98"; 368 } 369 else if (value.indexOf("nt") >= 0) 370 { 371 os = "windowsnt"; 372 } 373 else if (value.indexOf("2000") >= 0) 374 { 375 os = "windows2000"; 376 } 377 else if (value.indexOf("2003") >= 0) 378 { 379 os = "windows2003"; 380 } 381 else if (value.indexOf("xp") >= 0) 382 { 383 os = "windowsxp"; 384 } 385 else if (value.indexOf("ce") >= 0) 386 { 387 os = "windowsce"; 388 } 389 else if (value.indexOf("vista") >= 0) 390 { 391 os = "windowsvista"; 392 } 393 // will need better test here if any future Windows version has a 7 in it! 394 else if (value.indexOf("7") >= 0) 395 { 396 os = "windows7"; 397 } 398 return os; 399 } 400 else if (value.startsWith("linux")) 401 { 402 return "linux"; 403 } 404 else if (value.startsWith("aix")) 405 { 406 return "aix"; 407 } 408 else if (value.startsWith("digitalunix")) 409 { 410 return "digitalunix"; 411 } 412 else if (value.startsWith("hpux")) 413 { 414 return "hpux"; 415 } 416 else if (value.startsWith("irix")) 417 { 418 return "irix"; 419 } 420 else if (value.startsWith("macos") || value.startsWith("mac os")) 421 { 422 return "macos"; 423 } 424 else if (value.startsWith("netware")) 425 { 426 return "netware"; 427 } 428 else if (value.startsWith("openbsd")) 429 { 430 return "openbsd"; 431 } 432 else if (value.startsWith("netbsd")) 433 { 434 return "netbsd"; 435 } 436 else if (value.startsWith("os2") || value.startsWith("os/2")) 437 { 438 return "os2"; 439 } 440 else if (value.startsWith("qnx") || value.startsWith("procnto")) 441 { 442 return "qnx"; 443 } 444 else if (value.startsWith("solaris")) 445 { 446 return "solaris"; 447 } 448 else if (value.startsWith("sunos")) 449 { 450 return "sunos"; 451 } 452 else if (value.startsWith("vxworks")) 453 { 454 return "vxworks"; 455 } 456 return value; 457 } 458 459 public static String normalizeProcessor(String value) 460 { 461 value = value.toLowerCase(); 462 463 if (value.startsWith("x86-64") || value.startsWith("amd64") || 464 value.startsWith("em64") || value.startsWith("x86_64")) 465 { 466 return "x86-64"; 467 } 468 else if (value.startsWith("x86") || value.startsWith("pentium") 469 || value.startsWith("i386") || value.startsWith("i486") 470 || value.startsWith("i586") || value.startsWith("i686")) 471 { 472 return "x86"; 473 } 474 else if (value.startsWith("68k")) 475 { 476 return "68k"; 477 } 478 else if (value.startsWith("arm")) 479 { 480 return "arm"; 481 } 482 else if (value.startsWith("alpha")) 483 { 484 return "alpha"; 485 } 486 else if (value.startsWith("ignite") || value.startsWith("psc1k")) 487 { 488 return "ignite"; 489 } 490 else if (value.startsWith("mips")) 491 { 492 return "mips"; 493 } 494 else if (value.startsWith("parisc")) 495 { 496 return "parisc"; 497 } 498 else if (value.startsWith("powerpc") || value.startsWith("power") 499 || value.startsWith("ppc")) 500 { 501 return "powerpc"; 502 } 503 else if (value.startsWith("sparc")) 504 { 505 return "sparc"; 506 } 507 return value; 508 } 509 510 public static String normalizeOSVersion(String value) 511 { 512 // Header: 'Bundle-NativeCode', Parameter: 'osversion' 513 // Standardized 'osversion': major.minor.micro, only digits 514 try 515 { 516 return VersionRange.parse(value).toString(); 517 } 518 catch (Exception ex) 519 { 520 return Version.emptyVersion.toString(); 521 } 522 } 523 }