com.izforge.izpack.util.DefaultTargetPlatformFactory Maven / Gradle / Ivy
/*
* IzPack - Copyright 2001-2011 Julien Ponge, All Rights Reserved.
*
* http://izpack.org/ http://izpack.codehaus.org/
*
* Copyright 2011 Tim Anderson
*
* Licensed 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 com.izforge.izpack.util;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
/**
* Factory for constructing platform specific implementation implementations of interfaces or classes.
*
* It is configured using one or more TargetPlatformFactory.properties files, located in the class path
* under com/izforge/izpack/util/. A template is shown below.
*
* #
* # TargetPlatformFactory template.
* #
* # The format of each entry is as follows:
* #
* # <interface>[,name[,arch]] = <implementation>
* #
* # Where:
* # . interface - is the fully qualified interface or class name
* # . name - is the platform name corresponding to those defined in com.izforge.izpack.util.Platforms, as
* # lowercase
* # . arch - is the platform architecture corresponding to com.izforge.izpack.util.Platform.Arch, as
* # lowercase
* # . implementation - is the implementation of the interface for the platform
* #
* # E.g.:
* # com.izforge.izpack.util.os.NativeWrapper,windows = com.izforge.izpack.util.os.WinWrapper
* # com.izforge.izpack.util.os.NativeWrapper,windows,x64 = com.izforge.izpack.util.os.Win64Wrapper
* # com.izforge.izpack.util.os.NativeWrapper,windows_xp = com.izforge.izpack.util.os.Windows7Wrapper
* # com.izforge.izpack.util.os.NativeWrapper,windows_7,x86 = com.izforge.izpack.util.os.Windows7x86Wrapper
* # com.izforge.izpack.util.os.NativeWrapper,windows_7,x64 = com.izforge.izpack.util.os.Windows7x64Wrapper
* # com.izforge.izpack.util.os.NativeWrapper,unix = com.izforge.izpack.util.os.GenericUnixWrapper
* # com.izforge.izpack.util.os.NativeWrapper,debian_linux = com.izforge.izpack.util.os.DebianLinuxWrapper
* # com.izforge.izpack.util.os.NativeWrapper,mac_osx = com.izforge.izpack.util.os.MacOSXWrapper
* # com.izforge.izpack.util.os.NativeWrapper = com.izforge.izpack.util.os.DefaultWrapper
*
*
* @author Tim Anderson
* @see Platforms
* @see Platform
*/
public class DefaultTargetPlatformFactory implements TargetPlatformFactory
{
/**
* Map of interfaces to their corresponding platform implementations.
*/
private Map implementations = new HashMap();
/**
* The platforms.
*/
private Platforms platforms = new Platforms();
/**
* The TargetPlatformFactory properties.
*/
private static final String RESOURCE_PATH = "com/izforge/izpack/util/TargetPlatformFactory.properties";
/**
* Constructs a DefaultTargetPlatformFactory, configured from TargetPlatformFactory.properties
* resources.
*/
public DefaultTargetPlatformFactory()
{
try
{
Enumeration urls = getClass().getClassLoader().getResources(RESOURCE_PATH);
while (urls.hasMoreElements())
{
URL url = urls.nextElement();
try
{
add(url);
}
catch (IOException exception)
{
Debug.log(exception);
}
}
}
catch (IOException exception)
{
Debug.log(exception);
}
}
/**
* Creates a platform specific implementation of a class, for the current platform.
*
* @param clazz the class to create a platform specific instance of
* @return the instance for the specified platform
* @throws Exception for any error
*/
public T create(Class clazz) throws Exception
{
return create(clazz, platforms.getCurrentPlatform());
}
/**
* Creates a platform specific instance of a class, for the specified platform.
*
* @param clazz the class to create a platform specific instance of
* @param platform the platform
* @return the instance for the specified platform
* @throws Exception for any error
*/
@SuppressWarnings("unchecked")
public T create(Class clazz, Platform platform) throws Exception
{
Implementations impls = getImplementations(clazz);
if (impls == null)
{
throw new IllegalArgumentException("No implementations registered for class=" + clazz.getName());
}
Platform match = null;
Platform fallback = null;
for (Platform p : impls.getPlatforms())
{
if (p.equals(platform))
{
match = p;
break;
}
else if (platform.isA(p))
{
if (fallback == null || moreSpecific(platform, fallback, p))
{
fallback = p;
}
}
}
if (match == null)
{
if (fallback != null)
{
match = fallback;
}
}
String implName = (match != null) ? impls.getImplementation(match) : impls.getDefault();
if (implName == null)
{
throw new IllegalStateException("No implementation registered for class=" + clazz.getName()
+ " and platform=" + platform);
}
Class impl = Class.forName(implName);
if (clazz.isAssignableFrom(impl))
{
return (T) impl.newInstance();
}
throw new IllegalStateException(impl.getName() + " does not extend " + clazz.getName());
}
/**
* Adds the configuration from the specified URL.
*
* @param properties the configuration
* @param url the URL, for error reporting. May be null
*/
protected void add(Properties properties, URL url)
{
Parser parser = createParser(platforms, url);
parser.parse(properties, implementations);
}
/**
* Creates a new parser.
*
* @param platforms the platforms
* @param url the source URL
* @return a new parser
*/
protected Parser createParser(Platforms platforms, URL url)
{
return new Parser(platforms, url);
}
/**
* Returns the implementations registered for the specified class.
*
* @param clazz the class
* @return the corresponding implementations, or null if none are found
*/
protected Implementations getImplementations(Class clazz)
{
return implementations.get(clazz.getName());
}
/**
* Configures the factory from the specified URL.
*
* @param url the URL to load properties from
* @throws IOException if the properties cannot be read
*/
private void add(URL url) throws IOException
{
Properties properties = new Properties();
InputStream stream = url.openStream();
try
{
properties.load(stream);
}
finally
{
stream.close();
}
add(properties, url);
}
/**
* Determines if a platform is more specific than the current fallback platform.
*
* @param requested the requested platform
* @param fallback the current fallback platform
* @param platform the platform to check.
* @return true if platform is a fallback or has the same version as that requested
* and the fallback doesn't specify a version
*/
private boolean moreSpecific(Platform requested, Platform fallback, Platform platform)
{
boolean result = platform.isA(fallback);
if (!result)
{
if (requested.getVersion() != null && requested.getVersion().equals(platform.getVersion())
&& fallback.getVersion() == null)
{
result = true;
}
}
return result;
}
/**
* Properties parser.
*/
protected static class Parser
{
/**
* The platforms.
*/
private final Platforms platforms;
/**
* The source URL.
*/
private final URL url;
/**
* Constructs a Parser.
*
* @param platforms the platforms
* @param url the source URL
*/
public Parser(Platforms platforms, URL url)
{
this.platforms = platforms;
this.url = url;
}
/**
* Parse properties, adding them to the specified implementations.
*
* @param properties the properties to parse
* @param implementations the implementations to populate
*/
public void parse(Properties properties, Map implementations)
{
for (String key : properties.stringPropertyNames())
{
String[] interfacePlatform = key.split(",");
if (interfacePlatform.length >= 1 && interfacePlatform.length <= 3)
{
String iface = trimToNull(interfacePlatform[0]);
String name = (interfacePlatform.length >= 2) ? trimToNull(interfacePlatform[1]) : null;
String arch = (interfacePlatform.length == 3) ? trimToNull(interfacePlatform[2]) : null;
String impl = trimToNull(properties.getProperty(key));
if (iface == null)
{
warning("Ignoring null interface=" + key + " from " + url);
}
else if (impl == null)
{
warning("Ignoring null implementation for key=" + key + " from " + url);
}
else
{
Implementations impls = implementations.get(iface);
if (impls == null)
{
impls = new Implementations();
implementations.put(iface, impls);
}
if (name == null)
{
if (impls.getDefault() == null)
{
impls.setDefault(impl);
}
else
{
warning("Ignoring duplicate default implementation=" + impl + " from " + url);
}
}
else
{
Platform platform = platforms.getPlatform(name, arch);
if (platform.getName() == Platform.Name.UNKNOWN)
{
warning("Ignoring unsupported platform=" + platform + " for key=" + key + " from "
+ url);
}
else if (impls.getImplementation(platform) == null)
{
impls.addImplementation(platform, impl);
}
else
{
warning("Ignoring duplicate implementation=" + impl + " for platform=" + platform
+ " from " + url);
}
}
}
}
else
{
warning("Ignoring invalid entry=" + key + ", length=" + interfacePlatform.length + " from " + url);
}
}
}
/**
* Handles parser warnings.
*
* This implementation logs the message
*
* @param message the error message
*/
protected void warning(String message)
{
Debug.log("TargetPlatformFactory: " + message);
}
/**
* Trims a string to null if empty.
*
* @param str the string to trim. May be null
* @return the trimmed string. May be null
*/
private String trimToNull(String str)
{
if (str != null)
{
str = str.trim();
}
return (str == null || str.length() == 0) ? null : str;
}
}
/**
* Manages implementations of a class/interface, for multiple platforms.
*/
protected static class Implementations
{
/**
* Default implementation class name. May be null
*/
private String defaultImplementation;
/**
* The implementation class names, keyed on platform.
*/
private Map implementations = new HashMap();
/**
* Sets the default implementation class.
*
* @param defaultImplementation the default implementation class name. May be null
*/
public void setDefault(String defaultImplementation)
{
this.defaultImplementation = defaultImplementation;
}
/**
* Returns the default implementation class name.
*
* @return the default implementation class name. May be null
*/
public String getDefault()
{
return defaultImplementation;
}
/**
* Returns the platforms that have an implementation.
*
* @return the platforms that have an implementation
*/
public Set getPlatforms()
{
return implementations.keySet();
}
/**
* Returns the implementation for the specified platform.
*
* @param platform the platform
* @return the implementation, or null if none exists
*/
public String getImplementation(Platform platform)
{
return implementations.get(platform);
}
/**
* Adds an implementation for the specified platform.
*
* @param platform the platform
* @param implementation the implementation class name
*/
public void addImplementation(Platform platform, String implementation)
{
implementations.put(platform, implementation);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy