/*
* Copyright (C) 2012, Katy Hilgenberg.
* Special acknowledgments to: Knowledge & Data Engineering Group, University of Kassel (http://www.kde.cs.uni-kassel.de).
* Contact: sdcf@cs.uni-kassel.de
*
* This file is part of the SDCFramework (Sensor Data Collection Framework) project.
*
* The SDCFramework is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The SDCFramework is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SDCFramework. If not, see <http://www.gnu.org/licenses/>.
*/
package de.unikassel.android.sdcframework.preferences;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import android.content.Context;
import android.content.res.AssetManager;
import de.unikassel.android.sdcframework.data.SDCConfiguration;
import de.unikassel.android.sdcframework.data.SensorConfigurationEntry;
import de.unikassel.android.sdcframework.data.independent.GlobalSerializer;
import de.unikassel.android.sdcframework.devices.facade.SensorDeviceIdentifier;
import de.unikassel.android.sdcframework.devices.facade.SensorDevicePriorities;
import de.unikassel.android.sdcframework.preferences.facade.ApplicationPreferenceManager;
import de.unikassel.android.sdcframework.preferences.facade.SensorDeviceConfiguration;
import de.unikassel.android.sdcframework.preferences.facade.SensorDevicePreferences;
import de.unikassel.android.sdcframework.preferences.facade.ServiceConfiguration;
import de.unikassel.android.sdcframework.preferences.facade.TimeProviderConfiguration;
import de.unikassel.android.sdcframework.util.FileUtils;
import de.unikassel.android.sdcframework.util.Logger;
/**
* Implementation of a management component responsible to handle the
* preconfigured default configuration, respectively to read it from the file
* and update the default values in a preference manager instance. <br/<br/>
* The default configuration can be defined in an XML file, which is located in
* the projects asset folder. <br/>
* A configuration example is given in the documentation of the class
* {@linkplain SDCConfiguration SDCConfiguration}.
*
* @author Katy Hilgenberg
*
*/
public final class SDCConfigurationManager
{
/**
* The map with available device defaults
*/
private final Map< SensorDeviceIdentifier, SensorDeviceConfiguration > mapDeviceDefaults;
/**
* The service configuration
*/
private SDCConfiguration sdcConfiguration;
/**
* Constructor
*
* @param appContext
* the application context
* @param configFileName
* the configuration file name
*/
public SDCConfigurationManager( Context appContext, String configFileName )
{
super();
this.mapDeviceDefaults =
new HashMap< SensorDeviceIdentifier, SensorDeviceConfiguration >();
if ( !readDefaultsFromPrivateFileFolder( appContext, configFileName ) )
{
readDefaultsFromAssetFolder( appContext, configFileName );
}
}
/**
* Does read the defaults from the configuration file
*
* @param appContext
* the application context
* @param configFileName
* the configuration file name
*/
private final boolean readDefaultsFromPrivateFileFolder( Context appContext,
String configFileName )
{
try
{
String filePath =
appContext.getFilesDir().getPath() + File.separatorChar
+ configFileName;
InputStream is = new FileInputStream( FileUtils.fileFromPath( filePath ) );
readDefaults( is );
Logger.getInstance().info(
this,
String.format(
"External default configuration loaded from file %s.",
configFileName ) );
return true;
}
catch ( FileNotFoundException fnfe )
{}
catch ( Exception e )
{
Logger.getInstance().error(
this,
String.format(
"Failed to read default configuration from file %s in private folder. Reason: %s",
configFileName, e.getMessage() ) );
}
return false;
}
/**
* Does read the defaults from the configuration file
*
* @param appContext
* the application context
* @param configFileName
* the configuration file name
*/
private final void readDefaultsFromAssetFolder( Context appContext,
String configFileName )
{
AssetManager assetManager = appContext.getResources().getAssets();
try
{
InputStream is = assetManager.open( configFileName );
readDefaults( is );
}
catch ( Exception e )
{
Logger.getInstance().error(
this,
String.format(
"Failed to read default configuration from file %s in asset folder. Reason: %s",
configFileName, e.getMessage() ) );
}
}
/**
* Does read the defaults from an input stream.
*
* @param is
* the input stream
* @throws Exception
*/
private final void readDefaults( InputStream is ) throws Exception
{
mapDeviceDefaults.clear();
sdcConfiguration =
GlobalSerializer.serializeFromStream( SDCConfiguration.class, is );
// store device configuration default values
for ( SensorConfigurationEntry entry : sdcConfiguration.getListSensorConfigurations() )
{
// preset id with unknown device
SensorDeviceIdentifier id = SensorDeviceIdentifier.Unknown;
try
{
// try to read a valid identifier
id = Enum.valueOf( SensorDeviceIdentifier.class, entry.getSensorID() );
}
catch ( Exception e )
{
e.printStackTrace();
}
if ( SensorDeviceIdentifier.Unknown.equals( id ) )
{
// in case of errors log and continue with next entry
Logger.getInstance().error( this,
"Invalid sensor id in configuration file: " + entry.getSensorID() );
continue;
}
// ignore more than one entry for a sensor device
if ( mapDeviceDefaults.containsKey( id ) )
{
// in case of errors log and continue with next entry
Logger.getInstance().warning( this,
"Ignoring duplicate entry for sensor " + entry.getSensorID() );
continue;
}
// get device configuration default values and use internal defaults if
// they are not configured
boolean enabled = entry.getEnabled();
int frequency = entry.getFrequency();
if ( frequency <= 0 )
{
// ignore frequencies below or equal to 0
frequency = FrequencyPreference.DEFAULT;
}
// preset priority with internal default in case of corrupted entries
// or not configured values ( this will override configuration )
SensorDevicePriorities priorityLevel = PriorityLevelPreference.DEFAULT;
if ( entry.getPriority() != null )
{
try
{
priorityLevel =
Enum.valueOf( SensorDevicePriorities.class, entry.getPriority() );
}
catch ( Exception e )
{}
}
// add sensor configuration defaults to the device map
mapDeviceDefaults.put( id, new SensorDeviceConfigurationImpl(
frequency, priorityLevel, enabled ) );
}
Logger.getInstance().debug(
this, "Default configuration successfully loaded" );
}
/**
* Does update preference defaults by pre-configured defaults if available
*
* @param devicePreferences
* the sensor device to update defaults for
*/
private final void updateDefaults( SensorDevicePreferences devicePreferences )
{
SensorDeviceConfiguration defaults =
mapDeviceDefaults.get( devicePreferences.getDeviceIdentifier() );
if ( defaults != null )
{
devicePreferences.setDefault( defaults );
}
}
/**
* Does update defaults for the managed device preferences
*
* @param appPrefManager
* the application preference manager
*/
public final void
updateDefaults( ApplicationPreferenceManager appPrefManager )
{
// update device defaults by values from configuration file
for ( Entry< SensorDeviceIdentifier, SensorDeviceConfiguration > entry : mapDeviceDefaults.entrySet() )
{
updateDefaults( appPrefManager.getPreferencesForDevice( entry.getKey() ) );
}
// update service defaults by values from configuration file if available
appPrefManager.getServicePreferences().setDefault(
createServiceConfiguration( appPrefManager ) );
// update time provider defaults by values from configuration file if
// available
appPrefManager.getTimeProviderPreference().setDefault(
createTimeProviderConfiguration( appPrefManager ) );
}
/**
* Method to create a time provider default configuration from the XML file
* entries to update the application preference defaults
*
* @param appPrefManager
* the application preference manager
* @return an updated default configuration for the time provider preferences
*/
private TimeProviderConfiguration createTimeProviderConfiguration(
ApplicationPreferenceManager appPrefManager )
{
// update time provider
TimeProviderConfiguration tpConfig =
appPrefManager.getTimeProviderPreference().getDefault();
if ( sdcConfiguration != null )
{
tpConfig.update( sdcConfiguration.getTimeProviderConfigEntries() );
}
return tpConfig;
}
/**
* Method to create a service default configuration from the XML file
* preferences to update the application preference defaults
*
* @param appPrefManager
* the application preference manager
* @return an updated default configuration for the service preferences
*/
private ServiceConfiguration createServiceConfiguration(
ApplicationPreferenceManager appPrefManager )
{
ServiceConfiguration defaultConfiguration =
appPrefManager.getServicePreferences().getDefault();
if ( sdcConfiguration != null )
{
defaultConfiguration.update( sdcConfiguration );
}
return defaultConfiguration;
}
/**
* Method to clean up before class can be destroyed
*/
public final void onDestroy()
{
mapDeviceDefaults.clear();
sdcConfiguration = null;
}
/**
* Getter for the configured sensor devices
*
* @return a list of the configured sensor devices to be supported by the SDC
* service
*/
public Set< SensorDeviceIdentifier > getListDevices()
{
return Collections.unmodifiableSet( mapDeviceDefaults.keySet() );
}
}