/*
* Copyright (c) 2010-2012 Research In Motion Limited. All rights reserved.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License, Version 1.0,
* which accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*
*/
package net.rim.ejde.internal.launching;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import net.rim.ejde.internal.ui.launchers.LaunchUtils;
import net.rim.ejde.internal.util.VMUtils;
import net.rim.ide.OSUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.eclipse.jdt.launching.IVMInstall;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* @author dmeng
*/
public class DeviceProfileManager {
private static final String RIM_SIM_PATH = File.separator + "Research In Motion" + File.separator
+ "BlackBerry Device Simulators";
/** The logger for this class. */
private static Logger _logger = Logger.getLogger( DeviceProfileManager.class );
/**
* The String value indicating that fledge is installed in the same folder as the RC file if the value is given in the RC
* file.
*/
private static final String INSTALL_DIR = "<install_dir>";
private static final String FLEDGE_DAT = "fledge.dat";
/** The full path of the folders containing the RC file for the external BlackBerry device simulators. */
private String[] _commonSimDirs;
/** The singleton instance */
private static DeviceProfileManager _instance = null;
/** VM home, device list map */
private HashMap< String, List< DeviceInfo >> _deviceMap;
/** VM home, default device map */
private HashMap< String, DeviceInfo > _defaultDeviceMap;
/**
* Returns the singleton instance.
*
* @return The singleton instance
*/
public static DeviceProfileManager getInstance() {
if( _instance == null ) {
_instance = new DeviceProfileManager();
}
return _instance;
}
private DeviceProfileManager() {
_commonSimDirs = getExternalSimulatorsDirectory();
_deviceMap = new HashMap< String, List< DeviceInfo >>();
_defaultDeviceMap = new HashMap< String, DeviceInfo >();
}
/**
* Reload device info.
*
* @param vm
* the vm
*/
public void reloadDeviceInfo( IVMInstall vm ) {
DeviceInfo defaultDevice;
String vmId = vm.getId();
List< DeviceInfo > deviceList = new ArrayList< DeviceInfo >();
if( VMUtils.isInternal( vm ) ) {
deviceList.addAll( readLynxDevices( vm ) );
defaultDevice = readLynxDefaultSimulator( vm );
} else {
String simulatorPath = vm.getInstallLocation().getPath() + File.separator + "simulator";
deviceList.addAll( readRCFile( true, simulatorPath ) );
defaultDevice = readDefaultSimualtor( vm );
}
if( defaultDevice != null ) {
_defaultDeviceMap.put( vmId, defaultDevice );
}
for( int i = 0; i < _commonSimDirs.length; i++ ) {
deviceList.addAll( readRCFile( false, _commonSimDirs[ i ] ) );
}
_deviceMap.put( vmId, deviceList );
}
public List< DeviceInfo > getDeviceProfiles( IVMInstall vm ) {
String vmId = vm.getId();
List< DeviceInfo > deviceList = _deviceMap.get( vmId );
if( deviceList == null ) {
deviceList = new ArrayList< DeviceInfo >();
if( VMUtils.isInternal( vm ) ) {
deviceList.addAll( readLynxDevices( vm ) );
} else {
String simulatorPath = vm.getInstallLocation().getPath() + File.separator + "simulator";
deviceList.addAll( readRCFile( true, simulatorPath ) );
}
for( int i = 0; i < _commonSimDirs.length; i++ ) {
deviceList.addAll( readRCFile( false, _commonSimDirs[ i ] ) );
}
_deviceMap.put( vmId, deviceList );
}
return deviceList;
}
/**
* Retrieves all internal simulator profiles for the given SDK
*
* @param vm
* @return
*/
public List< DeviceInfo > getInternalDeviceProfiles( IVMInstall vm ) {
List< DeviceInfo > deviceProfiles = new ArrayList< DeviceInfo >();
if( VMUtils.isInternal( vm ) ) {
deviceProfiles.addAll( readLynxDevices( vm ) );
} else {
String simulatorPath = LaunchUtils.getSimualtorPath( vm );
deviceProfiles.addAll( readRCFile( true, simulatorPath ) );
}
return deviceProfiles;
}
/**
* Retrieves an external simulator profile for the given profile name
*
* @param profileName
* @return
*/
public DeviceInfo getExternalDeviceProfile( String profileName ) {
List< DeviceInfo > deviceProfiles = getExternalDeviceProfiles();
if( !StringUtils.isEmpty( profileName ) ) {
for( DeviceInfo deviceProfile : deviceProfiles ) {
if( deviceProfile.getBundleName().equalsIgnoreCase( profileName ) ) {
return deviceProfile;
}
}
}
return null;
}
/**
* Retrieves all external simulator profiles
*
* @return
*/
public List< DeviceInfo > getExternalDeviceProfiles() {
List< DeviceInfo > deviceProfiles = new ArrayList< DeviceInfo >();
for ( int i = 0; i < _commonSimDirs.length; i++ ) {
deviceProfiles.addAll( readRCFile( false, _commonSimDirs[i] ) );
}
return deviceProfiles;
}
/**
* Loads the RC file at the given directory and retrieves the device information from it.
*
* @param internal
* Is this the internal directory?
* @param directory
* the complete filepath of the directory containing the RC file to be read
*/
private List< DeviceInfo > readRCFile( boolean internal, String directory ) {
// Retrieves the RC file from the given directory. Exits the method if no RC file
// exists in the directory.
List< File > rcFiles = getRCFile( directory );
if( rcFiles.isEmpty() ) {
return new ArrayList< DeviceInfo >();
}
List< DeviceInfo > deviceList = new ArrayList< DeviceInfo >();
int size = rcFiles.size();
for( int i = 0; i < size; i++ ) {
File rcFile = rcFiles.get( i );
// Extracts each simulator directory from the RC file and validates each one.
try {
// Note: The parsing done here will have to be changed if the format of the RC file is changed.
// Right now it is assumed that the format of the line containing the simulator directory will
// be "SimulatorDirectory[device name]-[bundle name]=[simulator directory]"
Pattern pattern = Pattern
.compile( "SimulatorCommand(.+)-([^-]+)=(.+)fledge.exe(.+)\\s/handheld=(\\S+)(.+)/app-param=JvmAlxConfigFile:(\\S+)(.+)" );
String simulatorDirectory;
String configFile;
BufferedReader br = new BufferedReader( new FileReader( rcFile ) );
String line = br.readLine();
while( line != null ) {
Matcher matcher = pattern.matcher( line );
if( matcher.matches() ) {
simulatorDirectory = matcher.group( 3 );
// remove the last "\" character
simulatorDirectory = simulatorDirectory.substring( 0, simulatorDirectory.length() - 1 );
boolean isValid;
// If the simulator directory is "<install_dir>", then the directory containing the
// RC file is used for validation.
if( simulatorDirectory.equals( INSTALL_DIR ) ) {
simulatorDirectory = directory;
}
isValid = validate( simulatorDirectory );
// If the simulator directory exists and it contains fledge, then the information
// for the device is added to the list of available devices.
if( !isValid ) {
// return;
break;
}
String deviceName = matcher.group( 5 );
String bundleName;
if( internal ) {
bundleName = IFledgeLaunchConstants.DEFAULT_SIMULATOR_BUNDLE_NAME;
} else {
bundleName = rcFile.getName();
// remove .rc
bundleName = bundleName.substring( 0, bundleName.indexOf( ".rc" ) );
}
configFile = matcher.group( 7 );
// remove .xml
configFile = configFile.substring( 0, configFile.indexOf( ".xml" ) );
if( simulatorDirectory != null && simulatorDirectory.length() > 0 && bundleName != null
&& bundleName.length() > 0 && deviceName != null && deviceName.length() > 0 ) {
DeviceInfo deviceInfo = new DeviceInfo( bundleName, deviceName, simulatorDirectory, configFile );
deviceList.add( deviceInfo );
}
}
line = br.readLine();
}
} catch( Exception e ) {
_logger.error( e.getMessage() );
}
}
return deviceList;
}
/**
* Returns true if the given directory exists and if it contains fledge, or false otherwise.
*
* @param directory
* the directory to validate
* @return true if the given directory exists and if it contains fledge, or false otherwise
*/
private boolean validate( String directory ) {
File simulatorDirectory = new File( directory );
if( simulatorDirectory.exists() && simulatorDirectory.isDirectory() ) {
String[] files = simulatorDirectory.list();
return Arrays.asList( files ).contains( FLEDGE_DAT );
}
return false;
}
/**
* Returns a File object representing the RC file at the given directory, or null if there is no such file in there.
*
* @param directory
* the complete filepath of the directory containing the RC file to be returned
* @return a list of File object representing the RC files at the given directory, or empty list if there is no such file in
* there
*/
private List< File > getRCFile( String directory ) {
List< File > rcFiles = new ArrayList< File >();
File simulatorsDirectory = new File( directory );
if( simulatorsDirectory.exists() && simulatorsDirectory.isDirectory() ) {
File[] files = simulatorsDirectory.listFiles();
for( int i = 0; i != files.length; i++ ) {
String fileName = files[ i ].getName();
if( fileName.startsWith( "SimPackage" ) && fileName.endsWith( ".rc" ) ) {
// return files[i];
rcFiles.add( files[ i ] );
}
}
}
return rcFiles;
}
/**
* Returns the full path of the folders containing the RC file for the external BlackBerry device simulators.
* Note: If simulator installer is not run as administrator on Win-7 machine, the .rc file is stored in %LocalAppData% folder;
* the .rc file is stored in %CommonProgramFiles% folder on all other cases.
*/
private String[] getExternalSimulatorsDirectory() {
List< String > result = new ArrayList< String >();
String str;
if( OSUtils.isWindows() ) {
try {
// Executes a command to retrieve the filepath of the Common Files folder first.
Process p = Runtime.getRuntime().exec( "cmd /c echo %CommonProgramFiles%" );
BufferedReader br = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
str = br.readLine();
br.close();
str += RIM_SIM_PATH;
result.add( str );
p = Runtime.getRuntime().exec( "cmd /c echo %LocalAppData%" );
br = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
str = br.readLine();
br.close();
if( !str.equals( "%LocalAppData%" ) ) {
str += RIM_SIM_PATH;
result.add( str );
}
} catch( Exception e ) {
_logger.error( e.getMessage() );
}
}
return result.toArray( new String[ 0 ] );
}
/**
* Returns the default device info for the given VM. returns <code>null</code> if one is not found.
*
* @param IVMInstall
* The VM
* @return The default device info
*/
public DeviceInfo getDefaultDevice( IVMInstall vm ) {
String vmId = vm.getId();
DeviceInfo defaultDevice = _defaultDeviceMap.get( vmId );
if( defaultDevice == null ) {
if( VMUtils.isInternal( vm ) ) {
defaultDevice = readLynxDefaultSimulator( vm );
} else {
defaultDevice = readDefaultSimualtor( vm );
}
if( defaultDevice != null ) {
_defaultDeviceMap.put( vmId, defaultDevice );
}
}
// if there is no default device, pick the first one available
List< DeviceInfo > allDevices = getDeviceProfiles( vm );
if( defaultDevice == null || !allDevices.contains( defaultDevice ) ) {
defaultDevice = allDevices.get( 0 );
}
return defaultDevice;
}
/**
* Returns devices in the given Lynx VM.
*
* @param lynxVM
* The lynx VM
* @return The devices
*/
private List< DeviceInfo > readLynxDevices( IVMInstall lynxVM ) {
String vmId = lynxVM.getId();
List< DeviceInfo > devices = new ArrayList< DeviceInfo >();
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
String fledgePath = LaunchUtils.getFledgeExePath( lynxVM );
String simulatorPath = LaunchUtils.getSimualtorPath( lynxVM );
Document doc = db.parse( fledgePath + File.separator + "fledge-options.xml" );
doc.getDocumentElement().normalize();
NodeList optionNodeLst = doc.getElementsByTagName( "option" );
for( int i = 0; i < optionNodeLst.getLength(); i++ ) {
Node optionNode = optionNodeLst.item( i );
String name = ( optionNode.getAttributes().getNamedItem( "name" ) ).getNodeValue();
if( name.equals( "handheld" ) ) {
NodeList valueNodeList = ( (Element) optionNode ).getElementsByTagName( "value" );
if( valueNodeList.getLength() > 0 ) {
for( int j = 0; j < valueNodeList.getLength(); j++ ) {
Node valueNode = valueNodeList.item( j );
Node deviceNameNode = valueNode.getAttributes().getNamedItem( "name" );
if( deviceNameNode != null ) {
devices.add( new DeviceInfo( vmId, deviceNameNode.getNodeValue(), simulatorPath, deviceNameNode
.getNodeValue() ) );
} else {
devices.add( new DeviceInfo( vmId, valueNode.getTextContent(), simulatorPath, valueNode
.getTextContent() ) );
}
}
}
}
}
} catch( Exception e ) {
_logger.error( e );
}
return devices;
}
/**
* Returns the default device in the given Lynx VM.
*
* @param lynxVM
* The lynx VM
* @return The device
*/
private DeviceInfo readLynxDefaultSimulator( IVMInstall lynxVM ) {
String vmId = lynxVM.getId();
DeviceInfo defaultDevice = null;
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
String fledgePath = LaunchUtils.getFledgeExePath( lynxVM );
Document doc = db.parse( fledgePath + File.separator + "fledge-options.xml" );
doc.getDocumentElement().normalize();
NodeList optionNodeLst = doc.getElementsByTagName( "option" );
for( int i = 0; i < optionNodeLst.getLength(); i++ ) {
Node optionNode = optionNodeLst.item( i );
String name = ( optionNode.getAttributes().getNamedItem( "name" ) ).getNodeValue();
if( name.equals( "handheld" ) ) {
NodeList defaultNodeList = ( (Element) optionNode ).getElementsByTagName( "default" );
if( defaultNodeList.getLength() > 0 ) {
Node defaultNode = defaultNodeList.item( 0 );
String simulatorPath = lynxVM.getInstallLocation().getPath() + File.separator + "debug";
defaultDevice = new DeviceInfo( vmId, defaultNode.getTextContent(), simulatorPath,
defaultNode.getTextContent() );
break;
}
}
}
} catch( Exception e ) {
_logger.error( e );
}
return defaultDevice;
}
private DeviceInfo readDefaultSimualtor( IVMInstall vm ) {
DeviceInfo defaultDevice = null;
String simulatorPath = LaunchUtils.getSimualtorPath( vm );
File defaultSimulatorFile = new File( simulatorPath + File.separator + "defaultSimulator.bat" );
if( defaultSimulatorFile.exists() ) {
try {
Pattern pattern = Pattern.compile( "fledge.exe(.+)\\s/handheld=(\\S+)(.+)/app-param=JvmAlxConfigFile:(\\S+)(.+)" );
String configFile;
BufferedReader br = new BufferedReader( new FileReader( defaultSimulatorFile ) );
String line = br.readLine();
while( line != null ) {
Matcher matcher = pattern.matcher( line );
if( matcher.matches() ) {
String deviceName = matcher.group( 2 );
String bundleName = IFledgeLaunchConstants.DEFAULT_SIMULATOR_BUNDLE_NAME;
configFile = matcher.group( 4 );
// remove .xml
configFile = configFile.substring( 0, configFile.indexOf( ".xml" ) );
if( deviceName != null && deviceName.length() > 0 && configFile != null && configFile.length() > 0 ) {
defaultDevice = new DeviceInfo( bundleName, deviceName, simulatorPath, configFile );
break;
}
}
line = br.readLine();
}
} catch( Exception e ) {
_logger.error( e );
}
}
return defaultDevice;
}
}