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 }