blob: b59d18b9665ed3670b0b51684124b9179529554a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.felix.framework.util.manifestparser;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.felix.framework.Logger;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.VersionRange;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.Version;
public class R4LibraryClause
{
private static final String OS_AIX = "aix";
private static final String OS_DIGITALUNIX = "digitalunix";
private static final String OS_EPOC = "epoc32";
private static final String OS_HPUX = "hpux";
private static final String OS_IRIX = "irix";
private static final String OS_LINUX = "linux";
private static final String OS_MACOS = "macos";
private static final String OS_MACOSX = "macosx";
private static final String OS_NETBSD = "netbsd";
private static final String OS_NETWARE = "netware";
private static final String OS_OPENBSD = "openbsd";
private static final String OS_OS2 = "os2";
private static final String OS_QNX = "qnx";
private static final String OS_SOLARIS = "solaris";
private static final String OS_SUNOS = "sunos";
private static final String OS_VXWORKS = "vxworks";
private static final String OS_WINDOWS_2000 = "windows2000";
private static final String OS_WINDOWS_2003 = "windows2003";
private static final String OS_WINDOWS_7 = "windows7";
private static final String OS_WINDOWS_8 = "windows8";
private static final String OS_WINDOWS_9 = "windows9";
private static final String OS_WINDOWS_95 = "windows95";
private static final String OS_WINDOWS_98 = "windows98";
private static final String OS_WINDOWS_CE = "windowsce";
private static final String OS_WINDOWS_NT = "windowsnt";
private static final String OS_WINDOWS_SERVER_2008 = "windowsserver2008";
private static final String OS_WINDOWS_SERVER_2012 = "windowsserver2012";
private static final String OS_WINDOWS_VISTA = "windowsvista";
private static final String OS_WINDOWS_XP = "windowsxp";
private static final String OS_WIN_32 = "win32";
private static final String PROC_X86_64 = "x86-64";
private static final String PROC_X86 = "x86";
private static final String PROC_68K = "68k";
private static final String PROC_ARM_LE = "arm_le";
private static final String PROC_ARM_BE = "arm_be";
private static final String PROC_ARM = "arm";
private static final String PROC_ALPHA = "alpha";
private static final String PROC_IGNITE = "ignite";
private static final String PROC_MIPS = "mips";
private static final String PROC_PARISC = "parisc";
private static final String PROC_POWER_PC = "powerpc";
private static final String PROC_SPARC = "sparc";
/* Storing the OS names in a map as this is quicker to look up than a list.
*/
private static final Map<String, String> NORMALIZED_OS_NAMES;
static
{
Map<String, String> m = new HashMap<String, String>();
m.put(OS_AIX, "");
m.put(OS_DIGITALUNIX, "");
m.put(OS_HPUX, "");
m.put(OS_IRIX, "");
m.put(OS_LINUX, "");
m.put(OS_MACOS, "");
m.put(OS_NETBSD, "");
m.put(OS_NETWARE, "");
m.put(OS_OPENBSD, "");
m.put(OS_OS2, "");
m.put(OS_QNX, "");
m.put(OS_SOLARIS, "");
m.put(OS_SUNOS, "");
m.put(OS_VXWORKS, "");
m.put(OS_WINDOWS_2000, "");
m.put(OS_WINDOWS_2003, "");
m.put(OS_WINDOWS_7, "");
m.put(OS_WINDOWS_8, "");
m.put(OS_WINDOWS_9, "");
m.put(OS_WINDOWS_95, "");
m.put(OS_WINDOWS_98, "");
m.put(OS_WINDOWS_CE, "");
m.put(OS_WINDOWS_NT, "");
m.put(OS_WINDOWS_SERVER_2008, "");
m.put(OS_WINDOWS_SERVER_2012, "");
m.put(OS_WINDOWS_VISTA, "");
m.put(OS_WINDOWS_XP, "");
m.put(OS_WIN_32, "");
NORMALIZED_OS_NAMES = Collections.unmodifiableMap(m);
}
private final String[] m_libraryEntries;
private final String[] m_osnames;
private final String[] m_processors;
private final String[] m_osversions;
private final String[] m_languages;
private final String m_selectionFilter;
public R4LibraryClause(String[] libraryEntries, String[] osnames,
String[] processors, String[] osversions, String[] languages,
String selectionFilter)
{
m_libraryEntries = libraryEntries;
m_osnames = osnames;
m_processors = processors;
m_osversions = osversions;
m_languages = languages;
m_selectionFilter = selectionFilter;
}
public R4LibraryClause(R4LibraryClause library)
{
this(library.m_libraryEntries, library.m_osnames, library.m_osversions,
library.m_processors, library.m_languages,
library.m_selectionFilter);
}
public String[] getLibraryEntries()
{
return m_libraryEntries;
}
public String[] getOSNames()
{
return m_osnames;
}
public String[] getProcessors()
{
return m_processors;
}
public String[] getOSVersions()
{
return m_osversions;
}
public String[] getLanguages()
{
return m_languages;
}
public String getSelectionFilter()
{
return m_selectionFilter;
}
public boolean match(Map configMap) throws BundleException
{
String osName = (String) configMap.get(FelixConstants.FRAMEWORK_OS_NAME);
String processorName = (String) configMap.get(FelixConstants.FRAMEWORK_PROCESSOR);
String osVersion = (String) configMap.get(FelixConstants.FRAMEWORK_OS_VERSION);
String language = (String) configMap.get(FelixConstants.FRAMEWORK_LANGUAGE);
// Check library's osname.
if (!checkOSNames(osName, getOSNames()))
{
return false;
}
// Check library's processor.
if (!checkProcessors(processorName, getProcessors()))
{
return false;
}
// Check library's osversion if specified.
if ((getOSVersions() != null) &&
(getOSVersions().length > 0) &&
!checkOSVersions(osVersion, getOSVersions()))
{
return false;
}
// Check library's language if specified.
if ((getLanguages() != null) &&
(getLanguages().length > 0) &&
!checkLanguages(language, getLanguages()))
{
return false;
}
// Check library's selection-filter if specified.
if ((getSelectionFilter() != null) &&
(getSelectionFilter().length() >= 0) &&
!checkSelectionFilter(configMap, getSelectionFilter()))
{
return false;
}
return true;
}
private boolean checkOSNames(String osName, String[] osnames)
{
List<String> capabilityOsNames = getOsNameWithAliases(osName);
if (capabilityOsNames != null && osnames != null)
{
for (String curOsName : osnames)
{
if (capabilityOsNames.contains(curOsName))
{
return true;
}
}
}
return false;
}
private boolean checkProcessors(String processorName, String[] processors)
{
List<String> capabilitiesProcessors = getProcessorWithAliases(processorName);
if (capabilitiesProcessors != null && processors != null)
{
for (String currentProcessor : processors)
{
if (capabilitiesProcessors.contains(currentProcessor))
{
return true;
}
}
}
return false;
}
private boolean checkOSVersions(String osVersion, String[] osversions)
throws BundleException
{
Version currentOSVersion = Version.parseVersion(formatOSVersion(osVersion));
for (int i = 0; (osversions != null) && (i < osversions.length); i++)
{
try
{
VersionRange range = VersionRange.parse(osversions[i]);
if (range.isInRange(currentOSVersion))
{
return true;
}
}
catch (Exception ex)
{
throw new BundleException(
"Error evaluating osversion: " + osversions[i], ex);
}
}
return false;
}
private boolean checkLanguages(String currentLanguage, String[] languages)
{
for (int i = 0; (languages != null) && (i < languages.length); i++)
{
if (languages[i].equals(currentLanguage))
{
return true;
}
}
return false;
}
private boolean checkSelectionFilter(Map configMap, String expr)
throws BundleException
{
// Get all framework properties
Dictionary dict = new Hashtable();
for (Iterator i = configMap.keySet().iterator(); i.hasNext(); )
{
Object key = i.next();
dict.put(key, configMap.get(key));
}
// Compute expression
try
{
Filter filter = FrameworkUtil.createFilter(expr);
return filter.match(dict);
}
catch (Exception ex)
{
throw new BundleException(
"Error evaluating filter expression: " + expr, ex);
}
}
public static R4LibraryClause parse(Logger logger, String s)
{
try
{
if ((s == null) || (s.length() == 0))
{
return null;
}
s = s.trim();
if (s.equals(FelixConstants.BUNDLE_NATIVECODE_OPTIONAL))
{
return new R4LibraryClause(null, null, null, null, null, null);
}
// The tokens are separated by semicolons and may include
// any number of libraries along with one set of associated
// properties.
StringTokenizer st = new StringTokenizer(s, ";");
String[] libEntries = new String[st.countTokens()];
List osNameList = new ArrayList();
List osVersionList = new ArrayList();
List processorList = new ArrayList();
List languageList = new ArrayList();
String selectionFilter = null;
int libCount = 0;
while (st.hasMoreTokens())
{
String token = st.nextToken().trim();
if (token.indexOf('=') < 0)
{
// Remove the slash, if necessary.
libEntries[libCount] = (token.charAt(0) == '/')
? token.substring(1)
: token;
libCount++;
}
else
{
// Check for valid native library properties; defined as
// a property name, an equal sign, and a value.
// NOTE: StringTokenizer can not be used here because
// a value can contain one or more "=" too, e.g.,
// selection-filter="(org.osgi.framework.windowing.system=gtk)"
String property = null;
String value = null;
if (!(token.indexOf("=") > 1))
{
throw new IllegalArgumentException(
"Bundle manifest native library entry malformed: " + token);
}
else
{
property = (token.substring(0, token.indexOf("=")))
.trim().toLowerCase();
value = (token.substring(token.indexOf("=") + 1, token
.length())).trim();
}
// Values may be quoted, so remove quotes if present.
if (value.charAt(0) == '"')
{
// This should always be true, otherwise the
// value wouldn't be properly quoted, but we
// will check for safety.
if (value.charAt(value.length() - 1) == '"')
{
value = value.substring(1, value.length() - 1);
}
else
{
value = value.substring(1);
}
}
if (value != null)
{
value = value.toLowerCase();
}
// Add the value to its corresponding property list.
if (property.equals(Constants.BUNDLE_NATIVECODE_OSNAME))
{
osNameList.add(value);
}
else if (property.equals(Constants.BUNDLE_NATIVECODE_OSVERSION))
{
osVersionList.add(normalizeOSVersion(value));
}
else if (property.equals(Constants.BUNDLE_NATIVECODE_PROCESSOR))
{
processorList.add(value);
}
else if (property.equals(Constants.BUNDLE_NATIVECODE_LANGUAGE))
{
languageList.add(value);
}
else if (property.equals(Constants.SELECTION_FILTER_ATTRIBUTE))
{
// TODO: NATIVE - I believe we can have multiple selection filters too.
selectionFilter = value;
}
}
}
if (libCount == 0)
{
return null;
}
// Shrink lib file array.
String[] actualLibEntries = new String[libCount];
System.arraycopy(libEntries, 0, actualLibEntries, 0, libCount);
return new R4LibraryClause(
actualLibEntries,
(String[]) osNameList.toArray(new String[osNameList.size()]),
(String[]) processorList.toArray(new String[processorList.size()]),
(String[]) osVersionList.toArray(new String[osVersionList.size()]),
(String[]) languageList.toArray(new String[languageList.size()]),
selectionFilter);
}
catch (RuntimeException ex)
{
logger.log(Logger.LOG_ERROR,
"Error parsing native library header.", ex);
throw ex;
}
}
public static String formatOSVersion(String value)
{
// Header: 'Bundle-NativeCode', Parameter: 'osversion'
// Standardized 'osversion': major.minor.micro, only digits
try
{
Pattern versionPattern = Pattern.compile("\\d+\\.?\\d*\\.?\\d*");
Matcher matcher = versionPattern.matcher(value);
if (matcher.find())
{
value = matcher.group();
}
return Version.parseVersion(value).toString();
}
catch (Exception ex)
{
return Version.emptyVersion.toString();
}
}
public static List<String> getOsNameWithAliases(String osName)
{
//Can't assume this has been normalized
osName = normalizeOSName(osName);
List<String> result = null;
//
if (osName.startsWith("win"))
{
// Per spec windows ce does not include win32 alias
if (osName.equals(OS_WINDOWS_CE))
{
result = Arrays.asList("windowsce", "wince", "windows ce");
}
else
{
//Accumulate windows aliases. win32 may match many versions of windows.
Set<String> windowsOsList = new HashSet<String>();
if (osName.equals(OS_WINDOWS_95)|| osName.equals(OS_WIN_32))
{
windowsOsList.addAll(Arrays.asList("windows95", "win95",
"windows 95", OS_WIN_32));
}
if (osName.equals(OS_WINDOWS_98) || osName.equals(OS_WIN_32))
{
windowsOsList.addAll(Arrays.asList("windows98",
"windows 98", OS_WIN_32));
}
if (osName.equals(OS_WINDOWS_NT) || osName.equals(OS_WIN_32))
{
windowsOsList.addAll(Arrays.asList("windowsnt", "winnt",
"windows nt", OS_WIN_32));
}
if (osName.equals(OS_WINDOWS_2000) || osName.equals(OS_WIN_32))
{
windowsOsList.addAll(Arrays.asList("windows2000",
"win2000", "windows 2000", OS_WIN_32));
}
if (osName.equals(OS_WINDOWS_2003) || osName.equals(OS_WIN_32))
{
windowsOsList.addAll(Arrays.asList("windows2003",
"win2003", "windows 2003", OS_WIN_32,
"windows server 2003", "windowsserver2003"));
}
if (osName.equals(OS_WINDOWS_SERVER_2008) || osName.equals(OS_WIN_32))
{
windowsOsList.addAll(Arrays.asList("windows2008",
"win2008", "windows 2008", OS_WIN_32,
"windows server 2008", "windowsserver2008"));
}
if (osName.equals(OS_WINDOWS_SERVER_2012) || osName.equals(OS_WIN_32))
{
windowsOsList.addAll(Arrays.asList("windows2012",
"win2012", "windows 2012", OS_WIN_32,
"windows server 2012", "windowsserver2012"));
}
if (osName.equals(OS_WINDOWS_XP) || osName.equals(OS_WIN_32))
{
windowsOsList.addAll(Arrays.asList("windowsxp", "winxp",
"windows xp", OS_WIN_32));
}
if (osName.equals(OS_WINDOWS_VISTA) || osName.equals(OS_WIN_32))
{
windowsOsList.addAll(Arrays.asList("windowsvista",
"windows vista", OS_WIN_32));
}
if (osName.equals(OS_WINDOWS_7) || osName.equals(OS_WIN_32))
{
windowsOsList.addAll(Arrays.asList("windows7", "windows 7",
OS_WIN_32));
}
if (osName.equals(OS_WINDOWS_8) || osName.equals(OS_WIN_32))
{
windowsOsList.addAll(Arrays.asList("windows8", "windows 8",
OS_WIN_32));
}
if (osName.equals(OS_WINDOWS_9) || osName.equals(OS_WIN_32))
{
windowsOsList.addAll(Arrays.asList("windows9", "windows 9",
OS_WIN_32));
}
if (windowsOsList.isEmpty())
{
windowsOsList.add(osName);
}
result = new ArrayList<String>(windowsOsList);
}
}
else if (osName.equals(OS_LINUX))
{
result = Collections.singletonList(OS_LINUX);
}
else if (osName.equals(OS_AIX))
{
result = Collections.singletonList(OS_AIX);
}
else if (osName.equals(OS_DIGITALUNIX))
{
result = Collections.singletonList(OS_DIGITALUNIX);
}
else if (osName.equals(OS_EPOC))
{
result = Arrays.asList(OS_EPOC, "symbianos");
}
else if (osName.equals(OS_HPUX))
{
result = Arrays.asList(OS_HPUX, "hp-ux");
}
else if (osName.equals(OS_IRIX))
{
result = Collections.singletonList(OS_IRIX);
}
else if (osName.equals(OS_MACOSX))
{
result = Arrays.asList(OS_MACOSX, "mac os x");
}
else if (osName.equals(OS_MACOS))
{
result = Arrays.asList(OS_MACOS, "mac os");
}
else if (osName.equals(OS_NETWARE))
{
result = Collections.singletonList(OS_NETWARE);
}
else if (osName.equals(OS_OPENBSD))
{
result = Collections.singletonList(OS_OPENBSD);
}
else if (osName.equals(OS_NETBSD))
{
result = Collections.singletonList(OS_NETBSD);
}
else if (osName.equals(OS_OS2))
{
result = Arrays.asList(OS_OS2, "os/2");
}
else if (osName.equals(OS_QNX))
{
result = Arrays.asList(OS_QNX, "procnto");
}
else if (osName.equals(OS_SOLARIS))
{
result = Collections.singletonList(OS_SOLARIS);
}
else if (osName.equals(OS_SUNOS))
{
result = Collections.singletonList(OS_SUNOS);
}
else if (osName.equals(OS_VXWORKS))
{
result = Collections.singletonList(OS_VXWORKS);
}
else
{
result = Collections.singletonList(osName);
}
return Collections.unmodifiableList(result);
}
public static List<String> getProcessorWithAliases(String processor)
{
//Can't assume this has been normalized
processor = normalizeProcessor(processor);
List<String> result = null;
if (processor.equals(PROC_X86_64))
{
result = Arrays.asList(PROC_X86_64, "amd64", "em64t", "x86_64");
}
else if (processor.equals(PROC_X86))
{
result = Arrays.asList(PROC_X86, "pentium", "i386", "i486", "i586",
"i686");
}
else if (processor.equals(PROC_68K))
{
result = Arrays.asList(PROC_68K);
}
else if (processor.equals(PROC_ALPHA))
{
result = Arrays.asList(PROC_ALPHA);
}
else if (processor.equals(PROC_IGNITE))
{
result = Arrays.asList(PROC_IGNITE, "psc1k");
}
else if (processor.equals(PROC_MIPS))
{
result = Arrays.asList(PROC_MIPS);
}
else if (processor.equals(PROC_PARISC))
{
result = Arrays.asList(PROC_PARISC);
}
else if (processor.equals(PROC_POWER_PC))
{
result = Arrays.asList(PROC_POWER_PC, "power", "ppc");
}
else if (processor.equals(PROC_SPARC))
{
result = Arrays.asList(PROC_SPARC);
}
else
{
result = Collections.singletonList(processor);
}
return Collections.unmodifiableList(result);
}
public static String normalizeOSName(String value)
{
if (NORMALIZED_OS_NAMES.containsKey(value))
{
// Already normalized
return value;
}
value = value.toLowerCase();
if (value.startsWith("win"))
{
String os = "win";
if (value.indexOf("32") >= 0 || value.indexOf("*") >= 0)
{
os = OS_WIN_32;
}
else if (value.indexOf("95") >= 0)
{
os = OS_WINDOWS_95;
}
else if (value.indexOf("98") >= 0)
{
os = OS_WINDOWS_98;
}
else if (value.indexOf("nt") >= 0)
{
os = OS_WINDOWS_NT;
}
else if (value.indexOf("2000") >= 0)
{
os = OS_WINDOWS_2000;
}
else if (value.indexOf("2003") >= 0)
{
os = OS_WINDOWS_2003;
}
else if (value.indexOf("2008") >= 0)
{
os = OS_WINDOWS_SERVER_2008;
}
else if (value.indexOf("2012") >= 0)
{
os = OS_WINDOWS_SERVER_2012;
}
else if (value.indexOf("xp") >= 0)
{
os = OS_WINDOWS_XP;
}
else if (value.indexOf("ce") >= 0)
{
os = OS_WINDOWS_CE;
}
else if (value.indexOf("vista") >= 0)
{
os = OS_WINDOWS_VISTA;
}
else if ((value.indexOf(" 7") >= 0) || value.equals("win7"))
{
os = OS_WINDOWS_7;
}
else if ((value.indexOf(" 8") >= 0) || value.equals("win8"))
{
os = OS_WINDOWS_8;
}
else if ((value.indexOf(" 9") >= 0) || value.equals("win9"))
{
os = OS_WINDOWS_9;
}
return os;
}
else if (value.startsWith(OS_LINUX))
{
return OS_LINUX;
}
else if (value.startsWith(OS_AIX))
{
return OS_AIX;
}
else if (value.startsWith(OS_DIGITALUNIX))
{
return OS_DIGITALUNIX;
}
else if (value.startsWith(OS_HPUX))
{
return OS_HPUX;
}
else if (value.startsWith(OS_IRIX))
{
return OS_IRIX;
}
else if (value.startsWith(OS_MACOSX) || value.startsWith("mac os x"))
{
return OS_MACOSX;
}
else if (value.startsWith(OS_MACOS) || value.startsWith("mac os"))
{
return OS_MACOS;
}
else if (value.startsWith(OS_NETWARE))
{
return OS_NETWARE;
}
else if (value.startsWith(OS_OPENBSD))
{
return OS_OPENBSD;
}
else if (value.startsWith(OS_NETBSD))
{
return OS_NETBSD;
}
else if (value.startsWith(OS_OS2) || value.startsWith("os/2"))
{
return OS_OS2;
}
else if (value.startsWith(OS_QNX) || value.startsWith("procnto"))
{
return OS_QNX;
}
else if (value.startsWith(OS_SOLARIS))
{
return OS_SOLARIS;
}
else if (value.startsWith(OS_SUNOS))
{
return OS_SUNOS;
}
else if (value.startsWith(OS_VXWORKS))
{
return OS_VXWORKS;
}
return value;
}
public static String normalizeProcessor(String value)
{
value = value.toLowerCase();
if (value.startsWith(PROC_X86_64) || value.startsWith("amd64") ||
value.startsWith("em64") || value.startsWith("x86_64"))
{
return PROC_X86_64;
}
else if (value.startsWith(PROC_X86) || value.startsWith("pentium")
|| value.startsWith("i386") || value.startsWith("i486")
|| value.startsWith("i586") || value.startsWith("i686"))
{
return PROC_X86;
}
else if (value.startsWith(PROC_68K))
{
return PROC_68K;
}
else if (value.startsWith(PROC_ARM_LE))
{
return PROC_ARM_LE;
}
else if (value.startsWith(PROC_ARM_BE))
{
return PROC_ARM_BE;
}
else if (value.startsWith(PROC_ARM))
{
return PROC_ARM;
}
else if (value.startsWith(PROC_ALPHA))
{
return PROC_ALPHA;
}
else if (value.startsWith(PROC_IGNITE) || value.startsWith("psc1k"))
{
return PROC_IGNITE;
}
else if (value.startsWith(PROC_MIPS))
{
return PROC_MIPS;
}
else if (value.startsWith(PROC_PARISC))
{
return PROC_PARISC;
}
else if (value.startsWith(PROC_POWER_PC) || value.startsWith("power")
|| value.startsWith("ppc"))
{
return PROC_POWER_PC;
}
else if (value.startsWith(PROC_SPARC))
{
return PROC_SPARC;
}
return value;
}
public static String normalizeOSVersion(String value)
{
// Header: 'Bundle-NativeCode', Parameter: 'osversion'
// Standardized 'osversion': major.minor.micro, only digits
try
{
return VersionRange.parse(value).toString();
}
catch (Exception ex)
{
return Version.emptyVersion.toString();
}
}
}