// Copyright (c) 2004-2009 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:
// DST TZ Database Compiler
//
//
#include "TzGlobals.h"
#include <string>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <direct.h>
#include <iomanip>
#include <sys/stat.h>
#include "TZDocument.h"
#include "TZScanner.h"
#include "TzTables.h"
#include "TzHelpers.h"
using namespace std;
//============================================================================
// CTZDocument::CTZDocument
// Constructor.
//============================================================================
CTZDocument::CTZDocument(MScanner& aScanner)
: iScanner(aScanner)
{
//Create the root element
iRoot = new CTZElement();
iRoot->iParent = NULL;
iOpenedElement = iRoot;
//Create the tables
iRuleSetTable = new CTzCpRuleSetsTable(*this);
iRuleDefinitionTable = new CTzCpRuleDefinitionsTable(*this);
iStringTable = new CTzCpStringTable(*this);
iRegionsTable = new CTzCpRegionsTable(*this);
iRuleUseTable = new CTzCpRuleUsesTable(*this);
iZonesTable = new CTzCpZonesTable(*this);
iStdTimeAlignmentTable = new CTzCpStdTimeAlignmentsTable(*this);
iLinkTable = new CTzCpLinksTable(*this);
ReadConfigurationFile();
}
//============================================================================
// CTZDocument::~CTZDocument
// Destructor
//============================================================================
CTZDocument::~CTZDocument()
{
WriteConfigurationFile();
if (iRoot)
delete iRoot;
delete iRuleSetTable;
delete iRuleUseTable;
delete iRuleDefinitionTable;
delete iStringTable;
delete iRegionsTable;
delete iZonesTable;
delete iStdTimeAlignmentTable;
delete iLinkTable;
}
//============================================================================
// CTZDocument::Scan
// Delegates the actual file scanning to our CTzCpScanner object.
// returns KErrNone if succesful, or any TzErrorCode
//============================================================================
int CTZDocument::Scan(const char* aFileName)
{
return iScanner.Scan(aFileName);
}
//============================================================================
// CTZDocument::AddRootChildElement
// Adds a new element as a child of the root node
//============================================================================
void CTZDocument::CreateRootChildElement()
{
iOpenedElement = new CTZElement();
iOpenedElement->iParent = iRoot;
iRoot->NodeList().push_back(iOpenedElement);
}
//============================================================================
// CTZDocument::AddChildElement
// Creates a new CTZElement as a child node of iOpenedElement.
// iOpenedElement is then assigned to the new child node
//============================================================================
void CTZDocument::CreateChildElement()
{
iOpenedElement = iOpenedElement->CreateChildElement();
}
//============================================================================
// CTZDocument::AddAttribute
// Adds an attribute to the current node
//============================================================================
void CTZDocument::AddAttribute(const char* aValue)
{
iOpenedElement->AddAttribute(aValue);
}
//============================================================================
// CTZDocument::CloseElement
// Back up to the parent node
//============================================================================
void CTZDocument::CloseElement()
{
if (iOpenedElement != iRoot)
{
iOpenedElement = iOpenedElement->iParent;
}
}
//============================================================================
// CTZDocument::Assemble
// Iterates through the nodes assembling each one into the relevant T-Classes
//============================================================================
void CTZDocument::AssembleL()
{
cout.width(30);
CTzCpHelpers::PrintStep("Assembling nodes");
CTZNode* tmpNode;
string currentRuleSetName;
string currentZoneName;
int size = iRoot->NodeList().size();
for (int i = 0;i < size; i++)
{
tmpNode = iRoot->NodeList()[i];
if (tmpNode->CheckNode())
{
if (strcmpi(KOlsonRuleTag,tmpNode->NodeList()[0]->iValue.c_str()) == 0)
AssembleRuleL(*tmpNode);
else if (strcmpi(KOlsonLinkTag,tmpNode->NodeList()[0]->iValue.c_str()) == 0)
iLinkTable->AssembleL(*tmpNode); // Assemble Links
else if (strcmpi(KOlsonZoneTag,tmpNode->NodeList()[0]->iValue.c_str()) == 0)
AssembleZoneL(*tmpNode);
}
}
}
void CTZDocument::AssembleRuleL(CTZNode& aNode)
{
//Conditions tested here:
// 1: The 'To' year in a rule is >= the start year from the configuration file
// 2: The 'To' year in a rule is 'max'.
// 3: The 'To' year in a rule is 'only' but the from year is >= the start year from the configuration file
//If none of these conditions are met, just dump this rule (do nothing)
if ((atoi(aNode.NodeList()[ERuleFormatTo]->iValue.c_str()) >= TzGlobals::iStartYear) ||
(aNode.NodeList()[ERuleFormatTo]->iValue == "max") ||
(aNode.NodeList()[ERuleFormatTo]->iValue == "only" && atoi(aNode.NodeList()[ERuleFormatFrom]->iValue.c_str()) >= TzGlobals::iStartYear))
{
//==========================Rule Set==========================
//Get a RuleSet to add the RuleUse to
CTzCpRuleSet& aRuleSet = iRuleSetTable->AssembleL(aNode);
//=======================RuleDefinition=======================
//Assemble a rule definition from the node
CTzCpRuleDefinition& aRuleDefinition = iRuleDefinitionTable->AssembleL(aNode);
//=========================RuleLetter=========================
CTzCpString& aRuleLetter =iStringTable->AddString(aNode.NodeList()[ERuleFormatLetter]->iValue);
//==========================RuleUse===========================
CTzCpRuleUse& aRuleUse = iRuleUseTable->AssembleL(aNode);
aRuleUse.iRuleDefinitionPtr = &aRuleDefinition;
aRuleDefinition.IncrRefCount();
aRuleUse.iRuleLetterPtr = &aRuleLetter;
aRuleLetter.IncrRefCount();
//Add the RuleUse reference to the RuleSet
aRuleSet.AddRuleUse(aRuleUse);
aRuleUse.IncrRefCount();
}
}
void CTZDocument::AssembleZoneL(CTZNode& aNode)
{
//Get indexes for the region and zone name strings in the string table
string currentZoneName = aNode.NodeList()[EZoneFormatName]->iValue;
int slashChar = currentZoneName.find('/',0);
//'Region' is first part of a zone identity. eg 'Europe' in 'Europe/London'
//'Zone' is second part of a zone identity. eg 'London' in 'Europe/London'
string regionName;
if (slashChar > 0)
{
regionName = currentZoneName.substr(0,slashChar);
}
else
{
regionName = "";
}
CTzCpString& regionNameRef = iStringTable->AddString(regionName);
CTzCpString& zoneNameRef = iStringTable->AddString(currentZoneName.substr(slashChar+1));
//Start building the zone
CTzCpZone* newZone = iZonesTable->GetZone(zoneNameRef,regionNameRef,true);
newZone->IncrRefCount();
regionNameRef.IncrRefCount();
zoneNameRef.IncrRefCount();
//Get the region
CTzCpRegion& aRegion = iRegionsTable->GetRegion(regionNameRef);
//Add the zone to the regions zone index
aRegion.iRegionalZonesIndex->AddZoneIndex(*newZone);
newZone->IncrRefCount();
//======================Time Alignments=======================
//Process the zones time alignments
//Check date range
if ((EZoneFormatUntilYear >= aNode.NodeList().size()) ||
(atoi(aNode.NodeList()[EZoneFormatUntilYear]->iValue.c_str()) >= TzGlobals::iStartYear))
{
CTzCpString& timeZoneFormatName = iStringTable->AddString(aNode.NodeList()[EZoneFormatFormat]->iValue);
CTzCpStdTimeAlignment& aNewAlignment = iStdTimeAlignmentTable->AddTimeAlignment(timeZoneFormatName);
timeZoneFormatName.IncrRefCount();
aNewAlignment.AssembleL(aNode);
aNewAlignment.iOffsetToTimeZoneFormatName = &timeZoneFormatName;
//Offset to ruleset
aNewAlignment.iRuleSet = iRuleSetTable->GetRuleSet(aNode.NodeList()[EZoneFormatRules]->iValue);
aNewAlignment.iRuleSet->IncrRefCount();
//Add the new time alignment to the zone
newZone->iTimeAlignments.push_back(&aNewAlignment);
aNewAlignment.IncrRefCount();
}
//Do the same for the zones other time alignments
//A node value of "" indicates a child node
int size = aNode.NodeList().size();
for (int j = 0;j < size; j++)
{
CTZNode* tmpNode2 = aNode.NodeList()[j];
if ( tmpNode2->CheckNode() && (tmpNode2->iValue == ""))
{
//Check date range
if ((EZoneFormatUntilYear >= tmpNode2->NodeList().size()) ||
(atoi(tmpNode2->NodeList()[EZoneFormatUntilYear]->iValue.c_str()) >= TzGlobals::iStartYear))
{
CTzCpString& timeZoneFormatName = iStringTable->AddString(tmpNode2->NodeList()[EZoneFormatFormat]->iValue);
CTzCpStdTimeAlignment& aNewAlignment = iStdTimeAlignmentTable->AddTimeAlignment(timeZoneFormatName);
timeZoneFormatName.IncrRefCount();
aNewAlignment.AssembleL(*tmpNode2);
aNewAlignment.iOffsetToTimeZoneFormatName = &timeZoneFormatName;
//Offset to ruleset
string ruleSetName = aNode.NodeList()[EZoneFormatRules]->iValue;
aNewAlignment.iRuleSet = iRuleSetTable->GetRuleSet(tmpNode2->NodeList()[EZoneFormatRules]->iValue);
aNewAlignment.iRuleSet->IncrRefCount();
//Add the new time alignment to the zone
newZone->iTimeAlignments.push_back(&aNewAlignment);
aNewAlignment.IncrRefCount();
}
}
}
}
//============================================================================
// CTZDocument::Link
// Combines the format string from the stdtimealignment with the ruleuse
// letter. The resulting string is added to the string table and the offset
// to the string is stored back in the stdtimealignment.
// We can expect four different formatting cases eg
// 1: GMT/BST : Seperate the two strings and add to string table
// 2: C%sT : Replace %s with letter string from rule use
// 3: GMT : Add to string table if not already added
// 4: - : Ignore (remove %s)
//============================================================================
void CTZDocument::Link()
{
CTzCpHelpers::PrintStep("Linking");
string formatString;
//Iterate through all the STDTimeAlignments
int size = iStdTimeAlignmentTable->TimeAlignments().size();
for (int x = 0; x < size;x++)
{
CTzCpStdTimeAlignment* aTimeAlignment = iStdTimeAlignmentTable->TimeAlignments()[x];
string alignmentString = aTimeAlignment->iOffsetToTimeZoneFormatName->iString;
//Get the ruleset
if (aTimeAlignment->iPersistedEntity.iOffsetToRuleSet != KMaxTUint16)
{
CTzCpRuleSet* aRuleSet = aTimeAlignment->iRuleSet;
if (aRuleSet->RuleUses().size() == 0)
{
//Use the alignment string - there are no rule uses here
CTzCpString& stringRef = iStringTable->AddString(alignmentString);
aTimeAlignment->iOffsetsToTimeZonesShortNames.push_back(&stringRef);
stringRef.IncrRefCount();
}
else
{
int size2 = aRuleSet->RuleUses().size();
for (int z = 0; z < size2; z++)
{
CTzCpRuleUse* aRuleUse = iRuleUseTable->RuleUses()[z];
//Test for the following condition:
// Rule Use 'To' field is >= Time alignment 'From' field - AND
// Rule Use 'From' field is <= Time Alignment 'To' field
//We assume that the time alignment start field is the end year of the
//previous time alignment unless x == 0
int timeAlignmentStartYear = (x == 0) ? 0 : iStdTimeAlignmentTable->TimeAlignments()[x-1]->iPersistedEntity.iUntilYear;
if ((aRuleUse->iPersistedEntity.iUntilYear >= timeAlignmentStartYear) &&
(aRuleUse->iPersistedEntity.iFromYear <= aTimeAlignment->iPersistedEntity.iUntilYear))
{
string letterString = aRuleUse->iRuleLetterPtr->iString;
int slashPos = alignmentString.find_first_of('/');
int insertPos = alignmentString.find_first_of('%');
if (slashPos != -1)
{
formatString = alignmentString.substr(0,slashPos);
CTzCpString& formatStringRef = iStringTable->AddString(formatString);
aTimeAlignment->iOffsetsToTimeZonesShortNames.push_back(&formatStringRef);
formatStringRef.IncrRefCount();
formatString= alignmentString.substr(slashPos+1);
CTzCpString& stringRef = iStringTable->AddString(formatString);
aTimeAlignment->iOffsetsToTimeZonesShortNames.push_back(&stringRef);
stringRef.IncrRefCount();
}
else if (insertPos != -1)
{
formatString = alignmentString.substr(0,insertPos);
CTzCpString& formatStringRef = iStringTable->AddString(formatString);
aTimeAlignment->iOffsetsToTimeZonesShortNames.push_back(&formatStringRef);
formatStringRef.IncrRefCount();
if (letterString[0] != '-')
{
formatString += letterString;
}
formatString += alignmentString.substr(insertPos+2);
CTzCpString& stringRef = iStringTable->AddString(formatString);
aTimeAlignment->iOffsetsToTimeZonesShortNames.push_back(&stringRef);
stringRef.IncrRefCount();
}
}
}
}
}
}
//Iterate through the links - discarding invalid ones
std::vector<CTzCpLink*>::iterator iter = iLinkTable->Links().begin();
while(iter != iLinkTable->Links().end())
{
CTzCpZone* zone = iLinkTable->FindZone((*iter)->iLinkString->iString);
if (zone == NULL)
{
//Cannot find a valid zone for this link - discard
iter = iLinkTable->Links().erase(iter);
}
else
{
(*iter)->iLinkedZoneOffset = zone;
++iter;
}
}
}
//============================================================================
// CTZDocument:Exclude
// Removes unwanted entities from the database. Unwanted regions are specified
// in the compiler configuration file. Removing a region may cause certain
// database entities to become unreferenced. Each entity has a reference
// count, the entities will only be persisted if their reference count > 0.
//============================================================================
void CTZDocument::Exclude()
{
if (! TzGlobals::iIncludeAllRegions)
{
bool keep = false;
int size = iRegionsTable->Regions().size();
for (int x = 0; x < size; ++x)
{
CTzCpRegion& region = *iRegionsTable->Regions()[x];
TzGlobals::iAvailableRegions.push_back(region.iRegionNameRef->iString);
std::vector<string>::iterator it =
find(TzGlobals::iIncludedRegions.begin(),TzGlobals::iIncludedRegions.end(),region.iRegionNameRef->iString);
if (it == TzGlobals::iIncludedRegions.end())
{
//Need to remove this region from the region table,
//add it to the list of excluded regions and
//decrement all its zones reference counts by 1
TzGlobals::iExcludedRegions.push_back(region.iRegionNameRef->iString);
//Don't decrement the regions reference count - it will be 0 by default
region.iRegionNameRef->DecrRefCount();
region.iRegionalZonesIndex->DecrRefCount();
for (int y = 0; y < region.iRegionalZonesIndex->iZoneIndex.size();++y)
{
region.iRegionalZonesIndex->iZoneIndex[y]->DecrRefCount();
}
}
else
{
//Nothing really references a region, but we give it a reference count because we want to keep it
region.IncrRefCount();
}
}
}
else
{
//Using all the regions. We should still remove unreferenced database entries though
int size = iRegionsTable->Regions().size();
for (int x = 0; x < size; ++x)
{
CTzCpRegion& region = *iRegionsTable->Regions()[x];
TzGlobals::iAvailableRegions.push_back(region.iRegionNameRef->iString);
region.IncrRefCount();
}
}
iRegionsTable->RemoveUnreferencedEntities();
iZonesTable->RemoveUnreferencedEntities();
iStdTimeAlignmentTable->RemoveUnreferencedEntities();
iRuleSetTable->RemoveUnreferencedEntities();
iRuleUseTable->RemoveUnreferencedEntities();
iRuleDefinitionTable->RemoveUnreferencedEntities();
iStringTable->RemoveUnreferencedEntities();
}
//============================================================================
// CTZDocument::HandleScanError
//============================================================================
void CTZDocument::HandleScanError(const char* aFileName,int aLine,int aCol,char aChar)
{
stringstream os;
os << "Error in file " << aFileName << " line:\t" << aLine << "\tColumn:\t" << aCol << "\tUnexpected character:\t" << aChar;
iErrors.push_back(os.str());
}
//============================================================================
// CTZDocument::ExternaliseL
// Persist the data model to a file to be included in rom
//============================================================================
void CTZDocument::ExternaliseL()
{
ofstream aFile(TzGlobals::iOutputFilePath.c_str(),ios::out | ios::binary);
if (aFile.bad())
{
//Can't open file for writing - Abort
throw TzGlobals::ETzAbortCreateDatabaseFile;
}
//Write header space at start of file
aFile.write((char*)&iDbHeader,sizeof(iDbHeader));
//Strings
iStringTable->ExternaliseL(aFile);
// add extra characters to make the file size so far a multiple of 4
// and avoid potential byte alignment problems
int filePos = aFile.tellp();
if (filePos % 4)
{
char padChar = 0;
for (int i = 4; i > filePos % 4; i--)
{
aFile.write(&padChar,sizeof(char));
}
}
//RuleDefinitions
iRuleDefinitionTable->ExternaliseL(aFile);
//RuleUses
iRuleUseTable->ExternaliseL(aFile);
//RuleSets
iRuleSetTable->ExternaliseL(aFile);
//StdTimeAlignments
iStdTimeAlignmentTable->ExternaliseL(aFile);
//Zones
iZonesTable->ExternaliseL(aFile);
//Regions
iRegionsTable->ExternaliseL(aFile);
//Links (must be externalised after zones)
iLinkTable->ExternaliseL(aFile);
//After externalising all the tables, the header should now be complete.
//Reset to the beginning of the file and rewrite the header.
iDbHeader.iReserved1 = 0;
iDbHeader.iReserved2 = 0;
iDbHeader.iReserved3 = 0;
iDbHeader.iVersion = KTzDbVersion;
iDbHeader.iStartYear = TzGlobals::iStartYear;
iDbHeader.iReserved4 = 0;
iDbHeader.iOffsetToDefaultZone = GetDefaultZoneOffset();
aFile.seekp(0);
aFile.write((char*)&iDbHeader,sizeof(iDbHeader));
aFile.close();
}
//============================================================================
// CTZDocument::GetDefaultZoneOffset
// Finds the default zone specified in the configuration file, and returns the
// offset to this zone in the zone table
//============================================================================
TUint16 CTZDocument::GetDefaultZoneOffset() const
{
string tmpString;
if (strcmpi("Not Set",TzGlobals::iDefaultZoneName) == 0)
{
tmpString = iZonesTable->Zones()[0]->iRegionNameRef->iString;
tmpString += "/";
tmpString += iZonesTable->Zones()[0]->iZoneNameRef->iString;
cout << "Default zone not set. Using " << tmpString << " instead." <<endl;
}
else
{
tmpString = TzGlobals::iDefaultZoneName;
cout <<"Setting Default zone to " << TzGlobals::iDefaultZoneName << endl;
}
//Get default zone
int slashChar = tmpString.find('/',0);
string regStr;
string zoneStr;
if (slashChar > 0)
{
regStr = tmpString.substr(0,slashChar);
zoneStr = tmpString.substr(slashChar+1);
}
else
{
regStr = "";
zoneStr = tmpString;
}
CTzCpString& regionName = iStringTable->AddString(tmpString.substr(0,slashChar));
CTzCpString& zoneName = iStringTable->AddString(tmpString.substr(slashChar+1));
CTzCpZone* defaultZone = iZonesTable->GetZone(zoneName,regionName,false);
if (defaultZone != NULL)
{
cout << "Found matching zone\t";
cout << defaultZone->iRegionNameRef->iString << " ";
cout << defaultZone->iZoneNameRef->iString << endl;
}
else
{
CTzCpString& firstZone = iStringTable->AddString(iZonesTable->Zones()[0]->iZoneNameRef->iString);
CTzCpString& firstRegion = iStringTable->AddString(iZonesTable->Zones()[0]->iRegionNameRef->iString);
defaultZone = iZonesTable->GetZone(firstZone,firstRegion,false);
cout << TzGlobals::iDefaultZoneName << " not found. Using ";
cout << defaultZone->iRegionNameRef->iString;
cout << "/";
cout << defaultZone->iZoneNameRef->iString << " instead" << endl;
}
return defaultZone->iOffset;
}
//============================================================================
// CTZDocument::DisplayNodeList
// Prints out the node list to console
//============================================================================
void CTZDocument::DisplayNodeList() const
{
cout <<"Nodelist: "<< endl;
CTZNode* tmpNode;
CTZNode* tmpNode2;
int size = iRoot->NodeList().size();
for (int i = 0;i < size;i++)
{
tmpNode = iRoot->NodeList()[i];
int size2 = tmpNode->NodeList().size();
for (int j = 0; j < size2;j++)
{
tmpNode2 = tmpNode->NodeList()[j];
if (tmpNode2->iValue == "")
{
cout << endl << ".";
int size3 = tmpNode2->NodeList().size();
for (int k = 0; k < size3;k++)
{
cout << tmpNode2->NodeList()[k]->iValue << " ";
}
}
else
{
cout << tmpNode2->iValue << " ";
}
}
cout << endl;
}
}
//============================================================================
// CTZDocument::GetCommaSeperatedString
// Creates a comma seperated string from a string vector
//============================================================================
string CTZDocument::GetCommaSeparatedString(vector<string>& aStringVector)
{
string tmpString;
int size = aStringVector.size();
for (int j = 0; j < size;j++)
{
tmpString.append(aStringVector[j]);
if (j < size-1)
{
tmpString.append(",");
}
}
return tmpString;
}
//============================================================================
// CTZDocument::DisplayRegionsTable
// Prints out the regions table to console
//============================================================================
void CTZDocument::DisplayRegionsTable() const
{
int size = iRegionsTable->Regions().size();
for (int j = 0; j < size;j++)
{
cout << "Region " << j << ": " << iRegionsTable->Regions()[j]->iRegionNameRef->iString << endl;
}
}
//============================================================================
// CTZDocument::DisplayTimeAlignmentTable
// Prints out the Time Alignment table to console
//============================================================================
void CTZDocument::DisplayTimeAlignmentTable() const
{
int size = iStdTimeAlignmentTable->TimeAlignments().size();
for (int j = 0; j < size;j++)
{
cout << "RuleSets " << j << ": " << iStdTimeAlignmentTable->TimeAlignments()[j]->iOffsetToTimeZoneFormatName->iString << endl;
}
}
//============================================================================
// CTZDocument::DisplayZonesTable
// Prints out the zones table to console
//============================================================================
void CTZDocument::DisplayZonesTable() const
{
int size = iZonesTable->Zones().size();
for (int j = 0; j < size; j++)
{
cout << "Region " << ": " << iZonesTable->Zones()[j]->iRegionNameRef->iString;
cout << "\tZone: " << iZonesTable->Zones()[j]->iZoneNameRef->iString << endl;
}
}
//============================================================================
// CTZDocument::DisplayRuleSetTable
// Prints out the ruleset table to console
//============================================================================
void CTZDocument::DisplayRuleSetTable() const
{
int size = iRuleSetTable->RuleSets().size();
for (int j = 0; j < size;j++)
{
cout << "RuleSet " << iRuleSetTable->RuleSets()[j]->Name() << ": " << iRuleSetTable->RuleSets()[j]->RuleUses().size() << " rules\tRef:\t" << iRuleSetTable->RuleSets()[j]->ReferenceCount()<< endl;
int size2 = iRuleSetTable->RuleSets()[j]->RuleUses().size();
for (int k = 0; k < size2;k++)
{
cout << "RuleUse:\tFrom: " << iRuleUseTable->RuleUses()[k]->iPersistedEntity.iFromYear;
cout << "\tTo: " << iRuleUseTable->RuleUses()[k]->iPersistedEntity.iFromYear << endl;
}
}
}
//============================================================================
// CTZDocument::DisplayRuleDefinitionTable
// Prints out the rule definition table to console
//============================================================================
void CTZDocument::DisplayRuleDefinitionTable() const
{
int size = iRuleDefinitionTable->RuleDefinitions().size();
cout << size << " RULES DEFINED" << endl;
for (int z = 0; z < size; z++)
{
TTzRuleDefinition tmpDef = iRuleDefinitionTable->RuleDefinitions()[z]->iPersistedEntity;
cout << "Rule Definition: Saving: ";
cout << (int)(tmpDef.iStdTimeOffset);
cout << "\tTime: " << (int)(tmpDef.iTimeOfChange) << endl;
}
}
//============================================================================
// CTZDocument::DisplayStringTable
// Prints out the string table to console
//============================================================================
void CTZDocument::DisplayStringTable() const
{
int size = iStringTable->Strings().size();
for (int x = 0; x < size;x++)
{
cout << iStringTable->Strings()[x]->iString << endl;
}
}
//============================================================================
// CTZDocument::AssignZoneIds
// After all the zones have been assembled, we sort them alphabetically
// then assign each zone an id. The id will be read from the configuration
// file if the zone already exists. If this is a new zone, then the zone id
// will be equal to the next available zone id from the configuration file,
// and the next available zone id will be incremented.
//============================================================================
void CTZDocument::AssignZoneIds()
{
const int KMaxTzId = 0x3FFF; //Maximum numeric id that can be assigned to a zone
sort(iZonesTable->Zones().begin(),iZonesTable->Zones().end(),CTzCpZone::SZoneFullNameSort());
string fullZoneName;
int size = iZonesTable->Zones().size();
for (int i = 0; i < size;++i)
{
fullZoneName = iZonesTable->Zones()[i]->iRegionNameRef->iString;
if (fullZoneName.length() > 0)
{
fullZoneName += '/';
}
fullZoneName += iZonesTable->Zones()[i]->iZoneNameRef->iString;
int id = GetPrivateProfileInt("ZoneIdentities",fullZoneName.c_str(), KMaxTUint16, TzGlobals::iZoneIdIniFilePath.c_str());
if (id != KMaxTUint16)
{
iZonesTable->Zones()[i]->iLocationId = id;
}
else
{
iZonesTable->Zones()[i]->iLocationId = TzGlobals::iNextNumericZoneId;
if(TzGlobals::iNextNumericZoneId < KMaxTzId)
{
TzGlobals::iNextNumericZoneId += 8;
}
else
{
cout << "ERROR: All available numeric ids have been used" << endl;
throw TzGlobals::ETzAbortCreateDatabaseFile;
}
}
}
}
//============================================================================
// CTZDocument::VerifyZoneIds
// Checks that each zone id from TzIdentities.ini is present in the compiled
// database. If a zone is missing, a warning message is displayed. It is not
// an error for a zone to not be included in the database. Possible causes
// include a change in zone name in the Olsen data sources, or compilation
// of a custom database using a limited region set.
//============================================================================
void CTZDocument::VerifyZoneIds()
{
//Get the filesize of TzIdentities.ini
struct stat results;
int iniFileSizeBytes;
if (stat(TzGlobals::iZoneIdIniFilePath.c_str(), &results) == 0)
{
iniFileSizeBytes = results.st_size;
}
else
{
//It is very unlikely that the above call will fail, as we have already processed
//the TzIdentities.ini file by this point
cout << "WARNING: Could not retrieve TzIdentities.ini file size. Missing zone warnings will NOT be generated" << endl;
return;
}
//Create a char[] the same size as TzIdentities.ini to store the zone names
char* zoneBuffer = NULL;
zoneBuffer = new char[iniFileSizeBytes];
for (int i=0; i<iniFileSizeBytes; i++)
{
zoneBuffer[i] = 0; // Initialize all elements to zero.
}
//Retrieve all the zone names from TzIdentities.ini
GetPrivateProfileString("ZoneIdentities",0,"",zoneBuffer,iniFileSizeBytes,TzGlobals::iZoneIdIniFilePath.c_str());
//zoneBuffer now contains all the zone names, seperated by null char
//Split zoneBuffer into seperate zone names
string tmpString;
int numZones = iZonesTable->Zones().size();
int slashChar;
string regStr;
string zoneStr;
for (int x = 0; x < iniFileSizeBytes; ++x)
{
if (zoneBuffer[x] != 0)
{
tmpString += zoneBuffer[x];
}
else
{
//tmpString contains a full zone name. See if it is in the database
slashChar = tmpString.find('/',0);
//Split into Region and Zone
if (slashChar > 0)
{
regStr = tmpString.substr(0,slashChar);
zoneStr = tmpString.substr(slashChar+1);
}
else
{
regStr = "";
zoneStr = tmpString;
}
CTzCpString& regionName = iStringTable->AddString(regStr);
CTzCpString& zoneName = iStringTable->AddString(zoneStr);
if (iZonesTable->GetZone(zoneName,regionName,false) == NULL)
{
cout << "WARNING: Zone " << tmpString.c_str() << " not included in dataset" << endl;
}
tmpString.erase();
//The last key is followed by 2 null chars.
//If we find this we reached the last zone and can stop the search
if (zoneBuffer[x+1] == 0)
{
break;
}
}
}
delete [] zoneBuffer; // Free memory zoneBuffer char[]
zoneBuffer = NULL;
}
//============================================================================
// CTzDocument::CopyDatabaseFileToOutputDirectory
// Copies the created database file to the location specified in the
// configuration file. If there is no location specified, does nothing
//============================================================================
void CTZDocument::CopyDatabaseFileToOutputDirectory() const
{
char outputDir[KMaxPathLength];
GetPrivateProfileString("TzCompilerConfiguration","OutputDirectory","",outputDir,KMaxPathLength,TzGlobals::iIniFilePath.c_str());
if (strcmpi("",outputDir))
{
//Check the output directory exists - make it if necessary
string mkdir = "mkdir ";
mkdir += outputDir;
system(mkdir.c_str());
string copyString;
copyString = "copy ";
copyString += TzGlobals::iOutputFilePath;
copyString += " ";
copyString += outputDir;
cout << copyString.c_str() << endl;
system(copyString.c_str());
}
}
//============================================================================
// CTZDocument::ReadConfigurationFile
// Sets global variables from the configuration file.
//============================================================================
void CTZDocument::ReadConfigurationFile()
{
//Read the ini file to set up default parameters
char buffer[KMaxPathLength];
getcwd(buffer,KMaxPathLength);
cout.fill('.');
cout << setiosflags(ios::left);
std::string dataPath(buffer);
TzGlobals::iZoneIdIniFilePath = dataPath + "\\TZIdentities.ini";
dataPath += "\\Data";
char includeRegions[KMaxFiles];
char inputDir[KMaxPathLength];
char excludedFiles[KMaxFiles];
TzGlobals::iStartYear = GetPrivateProfileInt("TZCompilerConfiguration","EarliestDateOfInterest",KDefaultStartYear,TzGlobals::iIniFilePath.c_str());
if (TzGlobals::iStartYear < KMinStartYear || TzGlobals::iStartYear > KMaxStartYear)
{
TzGlobals::iStartYear = KDefaultStartYear; //invalid year was provided, set it to default value
}
int len = GetPrivateProfileString("TZCompilerConfiguration","DefaultZone","Not Set",TzGlobals::iDefaultZoneName,KMaxZoneNameLength,TzGlobals::iIniFilePath.c_str());
if (0 == len) //no DefaultZone was provided, set it to "Not Set"
{
strcpy(TzGlobals::iDefaultZoneName, "Not Set");
}
len = GetPrivateProfileString("TZCompilerConfiguration","IncludedRegions","All",includeRegions,KMaxFiles,TzGlobals::iIniFilePath.c_str());
if (0 == len) //no includeRegions was provided, set it to "All" - default value
{
strcpy(includeRegions, "All");
}
TzGlobals::iNextNumericZoneId = GetPrivateProfileInt("NextNumericZoneId","NextNumericZoneId",KFirstZoneNumericId,TzGlobals::iZoneIdIniFilePath.c_str());
GetPrivateProfileString("TzCompilerConfiguration","InputDirectory","",inputDir,KMaxPathLength,TzGlobals::iIniFilePath.c_str());
GetPrivateProfileString("Files","ExcludedFiles","",excludedFiles,KMaxFiles,TzGlobals::iIniFilePath.c_str());
//Set files to exclude
char* token;
token = strtok(excludedFiles,",");
while (token != 0)
{
TzGlobals::iExcludedFiles.push_back(token);
token = strtok(NULL,",");
}
if (strcmp(includeRegions, "All") != 0) //do not include all the regions
{
TzGlobals::iIncludeAllRegions = false;
token = strtok(includeRegions,",");
while (token != 0)
{
TzGlobals::iIncludedRegions.push_back(token);
token = strtok(NULL,",");
}
}
TzGlobals::iInputFilePath = inputDir;
if (TzGlobals::iInputFilePath.empty())
{
//Use current directory
TzGlobals::iInputFilePath += dataPath;
}
// Add a trailing backslash if it doesn't have one
int pathsize = TzGlobals::iInputFilePath.size();
if ((TzGlobals::iInputFilePath).find_last_of("\\") < pathsize-1)
{
TzGlobals::iInputFilePath.append("\\");
}
TzGlobals::iOutputFilePath = buffer;
TzGlobals::iOutputFilePath += "\\tzdb.dbz";
// region mask is not used at the moment
TzGlobals::iRegionMask = 0;
CTzCpHelpers::PrintStep("Using settings from ");
cout << TzGlobals::iIniFilePath.c_str() << endl;
CTzCpHelpers::PrintStep("Data directory is ");
cout << TzGlobals::iInputFilePath.c_str() << endl;
CTzCpHelpers::PrintStep("Start Year:");
cout << TzGlobals::iStartYear << endl;
}
//============================================================================
// CTZDocument::WriteConfigurationFile
// Update the configuration file to reflect the new next available id
// and display the time zone identity list
//============================================================================
void CTZDocument::WriteConfigurationFile()
{
//Update the next available numeric zone id
char tmp[256] = "";
sprintf(tmp,"%d",TzGlobals::iNextNumericZoneId);
WritePrivateProfileString("NextNumericZoneId","NextNumericZoneId",tmp,TzGlobals::iZoneIdIniFilePath.c_str());
WritePrivateProfileString("Regions","AvailableRegions",GetCommaSeparatedString(TzGlobals::iAvailableRegions).c_str(),TzGlobals::iIniFilePath.c_str());
WritePrivateProfileString("Regions","ExcludedRegions",GetCommaSeparatedString(TzGlobals::iExcludedRegions).c_str(),TzGlobals::iIniFilePath.c_str());
WritePrivateProfileString("Files","AvailableFiles",GetCommaSeparatedString(TzGlobals::iAvailableFiles).c_str(),TzGlobals::iIniFilePath.c_str());
// it is assumed that zones have been sorted by location ID before calling this
int numZones = iZonesTable->Zones().size();
string zoneString;
for (int i = 0; i < numZones; i++)
{
char id[256] = "";
sprintf(id,"%d",iZonesTable->Zones()[i]->iLocationId);
zoneString = iZonesTable->Zones()[i]->iRegionNameRef->iString;
if (zoneString.length() > 0)
{
zoneString += '/';
}
zoneString += iZonesTable->Zones()[i]->iZoneNameRef->iString;
WritePrivateProfileString("ZoneIdentities",zoneString.c_str(),id,TzGlobals::iZoneIdIniFilePath.c_str());
}
}
//============================================================================
// CTZDocument::DisplayData
// Display the data that have been parsed. Very useful for diagnostic purposes.
//============================================================================
void CTZDocument::DisplayData() const
{
//Have a look whats going on
DisplayNodeList();
DisplayRuleDefinitionTable();
DisplayRuleSetTable();
DisplayStringTable();
DisplayZonesTable();
DisplayTimeAlignmentTable();
}
//============================================================================
// End of file
//============================================================================