srcanaapps/depexplorer/com.nokia.s60tools.appdep/src/com/nokia/s60tools/appdep/core/model/CacheFactory.java
Licenses updated to EPL.
/*
* Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package com.nokia.s60tools.appdep.core.model;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import com.nokia.s60tools.appdep.common.ProductInfoRegistry;
import com.nokia.s60tools.appdep.core.data.CacheCompPropertyField;
import com.nokia.s60tools.appdep.core.data.CacheDataConstants;
import com.nokia.s60tools.appdep.core.data.CacheIndex;
import com.nokia.s60tools.appdep.exceptions.InvalidModelDataException;
import com.nokia.s60tools.appdep.exceptions.ZeroFunctionOrdinalException;
import com.nokia.s60tools.appdep.resources.Messages;
import com.nokia.s60tools.appdep.util.LogUtils;
import com.nokia.s60tools.util.debug.DbgUtility;
/**
* Singleton class that parses cache files and creates TargetCache object based
* on the parsed cache data.
*/
public class CacheFactory {
// Indices for using imported component definition line in dependencies cache that
// is of the following format:
//
// component_name.dll|61
//
public static final int USED_COMPONENT_NAME_FIELD_INDEX = 0;
public static final int IMPORTED_FUNCTION_CNT_FIELD_INDEX = 1;
// Indices for using library file definition line in symbols cache that
// is of the following format:
//
// W:\epoc32\release\armv5\lib\|component_name.dso|1188902018|2229
//
public static final int LIB_NAME_FIELD_INDEX = 1;
public static final int LIB_TIMESTAMP_FIELD_INDEX = 2;
public static final int EXPORT_FUNC_COUNT_FIELD_INDEX = 3;
private static final int LIB_DEF_LINE_FIELD_COUNT = 4;;
/**
* This regular expression is used to split cache data lines.
*/
private static final String CACHE_FIELD_SEPARATOR_REGEXP = getCacheFieldSeparatorRegExp();
/**
* Private exception used to notify about ending of cache file while doing reading.
*/
private class EOFException extends Exception{
private static final long serialVersionUID = -7745437229230851377L;
public EOFException(){
super();
}
}
/**
* Private helper class used for reading cache files.
*/
private class CacheFileReader{
private BufferedReader br;
private FileReader fileRdr;
/**
* Constructor.
* @param filePathName Absolute path name for the cache file to read from.
* @throws FileNotFoundException
*/
public CacheFileReader(String filePathName) throws FileNotFoundException{
File f = new File(filePathName);
fileRdr = new FileReader(f);
br = new BufferedReader(fileRdr);
}
/**
* Reads the contents of a file line by line.
* @throws EOFException
* @throws IOException
*/
public String nextLine() throws EOFException, IOException{
String line = br.readLine();
if(line == null){
// End-of-file reached
br.close();
fileRdr.close();
throw new EOFException();
}
return line;
}
}
/**
* Singleton instance of the factory class.
*/
private static CacheFactory instance;
/**
* Private constructor.
*/
private CacheFactory(){
}
/**
* Singleton class accessor method.
* @return Singleton instance of the class.
*/
public static CacheFactory getInstance(){
if(instance == null){
instance = new CacheFactory();
}
return instance;
}
/**
* Load a single cache data from dependencies and symbols cache file into internal data structure.
* @param cacheDirectoryAbsolutePath Absolute path name to the directory cache files for
* the target reside.
* @param targetPlatformId Id for the target platform to be loaded.
* @return Reference to data target data model service method interface.
* @param notifierRequestor If not <code>null</code> the client is requesting notifications about component loading process.
* @throws IOException
*/
public ITargetCache loadCache(String cacheDirectoryAbsolutePath, String targetPlatformId, ICacheLoadProgressNotification notifierRequestor) throws IOException{
TargetCache targetCache = new TargetCache(targetPlatformId);
String dependenciesCacheVersion = parseAndPopulateDependencies(targetCache, cacheDirectoryAbsolutePath + File.separator + ProductInfoRegistry.getCacheFileName(), notifierRequestor);
String symbolsCacheVersion = parseAndPopulateSymbols(targetCache, cacheDirectoryAbsolutePath + File.separator + ProductInfoRegistry.getCacheSymbolsFileName());
// Cache file versions must match
if(! dependenciesCacheVersion.equals(symbolsCacheVersion)){
String errMsg = Messages.getString("CacheFactory.CacheDataIntegriryFailure_ErrMsg") //$NON-NLS-1$
+ " " + Messages.getString("CacheFactory.CacheDataIntegriryFailure_Version1_ErrMsg") //$NON-NLS-1$ //$NON-NLS-2$
+ dependenciesCacheVersion + Messages.getString("CacheFactory.CacheDataIntegriryFailure_Version2_ErrMsg") //$NON-NLS-1$
+ symbolsCacheVersion + Messages.getString("CacheFactory.CacheDataIntegriryFailure_NoMatch_ErrMsg");//$NON-NLS-1$
LogUtils.logStackTrace(errMsg, null);
throw new RuntimeException(errMsg);
}
targetCache.setVersion(dependenciesCacheVersion); // Storing cache version, if all versions are matching
return targetCache;
}
/**
* Parses and populates dependencies cache data.
* @param targetCache Target cache to set dependencies cache data.
* @param dependenciesCacheAbsoluteFilePathName Absolute path name to cache data file.
* @param notifierRequestor If not <code>null</code> the client is requesting notifications about component loading process.
* @return Version number of the cache file.
* @throws IOException
*/
private String parseAndPopulateDependencies(TargetCache targetCache,
String dependenciesCacheAbsoluteFilePathName, ICacheLoadProgressNotification notifierRequestor) throws IOException {
DependenciesCache dependenciesCache = new DependenciesCache();
String cacheVersion = null;
String line = null;
String prevLine = null;
ComponentPropertiesData currentCmpPropData = null;
try {
CacheFileReader cacheRdr = new CacheFileReader(dependenciesCacheAbsoluteFilePathName);
line = cacheRdr.nextLine();
cacheVersion = getVersionInfoFromHeader(line, dependenciesCacheAbsoluteFilePathName);
while(true){ // Reading lines until EOFException or other exception encountered
prevLine = line;
line = cacheRdr.nextLine();
String[] stringArrayCandidate = splitPopertyLineIntoStringArray(line);
if(isComponentDefinitionLineInMainCacheFile(stringArrayCandidate)){
currentCmpPropData = new ComponentPropertiesData(stringArrayCandidate);
handleComponentReferences(cacheRdr, currentCmpPropData);
dependenciesCache.addComponentPropertiesData(currentCmpPropData,targetCache.getId());
if(notifierRequestor != null){
notifierRequestor.componentLoaded(currentCmpPropData.getFilename());
}
}
}
} catch (EOFException e) {
// Cache file must end with CacheIndex.CACHE_FILE_END_MARK in order being fully generated and valid one
if(!prevLine.equals(CacheDataConstants.CACHE_FILE_END_MARK)){
String errMsg = Messages.getString("CacheFactory.CacheDataIntegriryFailure_ErrMsg") //$NON-NLS-1$
+ " " + Messages.getString("CacheFactory.CacheDataIntegriryFailure_version_NoEOF_Mark_ErrMsg") //$NON-NLS-1$ //$NON-NLS-2$
+ dependenciesCacheAbsoluteFilePathName + ").";//$NON-NLS-1$
LogUtils.logStackTrace(errMsg, e);
throw new RuntimeException(errMsg);
}
} catch (Exception e) {
String errMsg = Messages.getString("CacheFactory.CacheDataLoadFailure_ErrMsg") //$NON-NLS-1$
+ dependenciesCacheAbsoluteFilePathName + Messages.getString("CacheFactory.CacheDataLoadFailure_Unexpected_ErrMsg") //$NON-NLS-1$
+ e.getClass().getSimpleName()
+ "): " //$NON-NLS-1$
+ e.getMessage();
LogUtils.logStackTrace(errMsg, e);
throw new RuntimeException(errMsg);
}
// Setting dependencies cache data
targetCache.setDependenciesCache(dependenciesCache);
return cacheVersion;
}
/**
* Parses and populates symbols cache data.
* @param targetCache Target cache to set dependencies cache data.
* @param symbolsCacheAbsoluteFilePathName Absolute path name to cache data file.
* @return Version number of the cache file.
* @throws IOException
*/
private String parseAndPopulateSymbols(TargetCache targetCache,
String symbolsCacheAbsoluteFilePathName) throws IOException {
SymbolsCache symbolsCache = new SymbolsCache();
String cacheVersion = null;
String prevLine = null;
ComponentPropertiesData currentCmpPropData = null;
try {
CacheFileReader cacheRdr = new CacheFileReader(symbolsCacheAbsoluteFilePathName);
String line = cacheRdr.nextLine();
cacheVersion = getVersionInfoFromHeader(line, symbolsCacheAbsoluteFilePathName);
while(true){ // Reading lines until EOFException or other exception encountered
prevLine = line;
line = cacheRdr.nextLine();
String[] stringArrayCandidate = splitPopertyLineIntoStringArray(line);
// Checking if we have library definition line
if(isComponentDefinitionLineSymbolsCacheFile(stringArrayCandidate)){
// Getting field from the library definition line
String libBaseName = SymbolsCache.removeFileExtension(stringArrayCandidate[LIB_NAME_FIELD_INDEX]);
long libCachedTimestamp = Long.parseLong(stringArrayCandidate[LIB_TIMESTAMP_FIELD_INDEX]);
int exportedFuncCount = Integer.parseInt(stringArrayCandidate[EXPORT_FUNC_COUNT_FIELD_INDEX]);
// Creating library data object
LibPropertiesData libPropData = new LibPropertiesData(libBaseName, libCachedTimestamp);
// Adding exported function into library data object
handleExportedFunctions(cacheRdr, libPropData, exportedFuncCount);
// Adding to symbols cache
symbolsCache.addLibPropertiesData(libPropData);
}
}
} catch (EOFException e) {
// Cache file must end with CacheIndex.CACHE_FILE_END_MARK in order being fully generated and valid one
if(!prevLine.equals(CacheDataConstants.CACHE_FILE_END_MARK)){
String errMsg = Messages.getString("CacheFactory.CacheDataIntegriryFailure_ErrMsg") //$NON-NLS-1$
+ " " + Messages.getString("CacheFactory.CacheDataIntegriryFailure_NoEOF_Mark_ErrMsg") //$NON-NLS-1$ //$NON-NLS-2$
+ symbolsCacheAbsoluteFilePathName + ").";//$NON-NLS-1$
LogUtils.logStackTrace(errMsg, e);
throw new RuntimeException(errMsg);
}
} catch (InvalidModelDataException e) {
String errMsg = Messages.getString("CacheFactory.CacheDataIntegriryFailure_ErrMsg") //$NON-NLS-1$
+ " " + Messages.getString("CacheFactory.CacheDataIntegriryFailure_InvalidComponent_ErrMsg") //$NON-NLS-1$ //$NON-NLS-2$
+ symbolsCacheAbsoluteFilePathName + Messages.getString("CacheFactory.CacheDataIntegriryFailure_Detailed_ErrMsg") //$NON-NLS-1$
+ currentCmpPropData;
LogUtils.logStackTrace(errMsg, e);
throw new RuntimeException(errMsg);
}
// Setting symbols cache data
targetCache.setSymbolsCache(symbolsCache);
return cacheVersion;
}
/**
* Reads exported functions and stores into model
* @param cacheRdr Cache data reader object
* @param libPropData Library properties data object to add exported functions.
* @param exportedFuncCount Exported function count read from the reader object.
* @throws IOException
* @throws EOFException
*/
private void handleExportedFunctions(CacheFileReader cacheRdr, LibPropertiesData libPropData, int exportedFuncCount) throws EOFException, IOException {
for (int exportFuncOrdinal = 1; exportFuncOrdinal <= exportedFuncCount; exportFuncOrdinal++) {
String exportFuncName = cacheRdr.nextLine();
Integer exportFuncOrdinalAsInteger = new Integer(exportFuncOrdinal);
ExportFunctionData exportFunctionData;
try {
exportFunctionData = new ExportFunctionData(exportFuncOrdinalAsInteger.toString(), exportFuncName);
libPropData.addExportedFunction(exportFunctionData);
} catch (ZeroFunctionOrdinalException e) {
// Zero ordinal functions are just ignored and not added to properties data.
DbgUtility.println(DbgUtility.PRIORITY_LOOP, "Encountered zero ordinal exported function: " + e.getFunctionName()); //$NON-NLS-1$
}
}
}
/**
* This method is created purely for enabling JUnit testing of ITargetCache interface
* with a given set of test data without requiring parsing of
* @param version Cache file data format version.
* @param id Id of the target platform this target cache represents
* @param dependenciesCache Dependencies cache data.
* @param symbolsCache Library cache data.
* @return Reference to data target data model service method interface.
*/
public ITargetCache createCacheForUnitTests(String version, String id, DependenciesCache dependenciesCache, SymbolsCache symbolsCache){
TargetCache cache = new TargetCache(id);
cache.setDependenciesCache(dependenciesCache);
cache.setSymbolsCache(symbolsCache);
cache.setVersion(version);
return cache;
}
/**
* Reads version information from the header line
* @param headerLine Header line to be checked version info from.
* @param cacheFileAbsolutePathName
* @return Version string.
*/
private String getVersionInfoFromHeader(String headerLine, String cacheFileAbsolutePathName) {
String[] splitArr = headerLine.split(CacheDataConstants.CACHE_VERSION_INFO_SEPARATOR);
if(splitArr.length == 2){
return splitArr[1].trim();
}
String errMsg = Messages.getString("CacheFactory.CacheDataIntegriryFailure_ErrMsg") //$NON-NLS-1$
+ " " + Messages.getString("CacheFactory.CacheDataIntegriryFailure_NoVersionNumber_ErrMsg") //$NON-NLS-1$ //$NON-NLS-2$
+ cacheFileAbsolutePathName + ")."; //$NON-NLS-1$
LogUtils.logStackTrace(errMsg, null);
throw new RuntimeException(errMsg);
}
/**
* Checks if the current line is a component definition line array.
* Line is component property line if the field count is the
* one that is expected for this kind of line.
* @param stringArrayCandidate String array candidate.
* @return <code>true</code> if component definition line, otherwise <code>false</code>.
*/
boolean isComponentDefinitionLineInMainCacheFile(String[] stringArrayCandidate) {
if(stringArrayCandidate.length == CacheCompPropertyField.getCompPropertyFieldCount()){
return true;
}
return false;
}
/**
* Splits the property line into a string array containing property line fields.
* @param compPropertiesLine Property line to be split.
* @return A string array containing property line fields.
*/
String[] splitPopertyLineIntoStringArray(String compPropertiesLine){
return compPropertiesLine.split(CACHE_FIELD_SEPARATOR_REGEXP);
}
/**
* Stores all the components from which this component property data object imports functions.
* @param cacheRdr Cache data reader.
* @param currentCmpPropData Component properties object into which add imported functions and their components.
* @throws IOException
* @throws EOFException
*/
private void handleComponentReferences(CacheFileReader cacheRdr,
ComponentPropertiesData currentCmpPropData) throws EOFException, IOException {
// Getting amount of referred components
int referredCompCount = currentCmpPropData.getDllRefTableCountAsInt();
// Storing information from the all referred components.
for (int handledCmpCount = 0; handledCmpCount < referredCompCount; handledCmpCount++) {
// Getting component name and count of imported functions
String referredCompLine = cacheRdr.nextLine();
String [] referredCompArr = splitPopertyLineIntoStringArray(referredCompLine);
String referredCmpName = referredCompArr[USED_COMPONENT_NAME_FIELD_INDEX];
String importedFuncCountAsString = referredCompArr[IMPORTED_FUNCTION_CNT_FIELD_INDEX];
int importedFuncCount = Integer.parseInt(importedFuncCountAsString);
// Adding data model object for storing functions imported from the component
UsedComponentData usedCmpData = new UsedComponentData(referredCmpName);
// Temporary variables used for access textual cache data
String importedFuncLine;
String[] importedFuncLineArr;
boolean isVirtual;
// Browsing through all the imported functions for the component
for (int i = 0; i < importedFuncCount; i++) {
importedFuncLine = cacheRdr.nextLine();
importedFuncLineArr = splitPopertyLineIntoStringArray(importedFuncLine);
isVirtual = isVirtualFunctionFlagSet(importedFuncLineArr);
ImportFunctionData importFuncData;
try {
importFuncData = new ImportFunctionData(
importedFuncLineArr[ImportFunctionData.ORDINAL_FIELD_INDEX],
importedFuncLineArr[ImportFunctionData.NAME_FIELD_INDEX],
isVirtual,
importedFuncLineArr[ImportFunctionData.OFFSET_FIELD_INDEX]
);
usedCmpData.addImportedFunction(importFuncData);
} catch (ZeroFunctionOrdinalException e) {
// Zero ordinal functions are just ignored and not added to properties data.
DbgUtility.println(DbgUtility.PRIORITY_LOOP, "Encountered zero ordinal imported function: " //$NON-NLS-1$
+ e.getFunctionName());
}
} // for
// After imported function are added, adding imported component to the using component object
currentCmpPropData.addUsedComponentData(usedCmpData);
} // for
}
/**
* Checks from given imported function info array if the function
* is virtual.
* The call is further delegated to CacheIndex class
* @param importedFuncLineArr Function info array
* @return <code>true</code> if the function is virtual,
* otherwise <code>false</code>.
*/
public static boolean isVirtualFunctionFlagSet(String[] importedFuncLineArr) {
return CacheIndex.isVirtualFunctionFlagSet(importedFuncLineArr);
}
/**
* Returns regular expression used to split fields in cache data.
* The call is further delegated to CacheIndex class
* @return regular expression used to split fields in cache data.
*/
private static String getCacheFieldSeparatorRegExp() {
return CacheIndex.getCacheFieldSeparatorRegExp();
}
/**
* Checks if the current line is a library definition line.
* @param stringArrayCandidate Line to be checked.
* @return <code>true</code> if component definition line, otherwise <code>false</code>.
*/
private boolean isComponentDefinitionLineSymbolsCacheFile(String[] stringArrayCandidate) {
if(stringArrayCandidate.length == LIB_DEF_LINE_FIELD_COUNT){
return true;
}
return false;
}
}