--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/srcanaapps/depexplorer/com.nokia.s60tools.appdep/src/com/nokia/s60tools/appdep/core/model/CacheFactory.java Sat Jan 09 10:04:11 2010 +0530
@@ -0,0 +1,456 @@
+/*
+* 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;
+ }
+}