/// <summary> /// Creates a new OlsonParser to parse the provided /// Olson/TZ Database directory into Rules /// and TimeZones. The directory provided must be a valid TZ Database. /// </summary> /// <param name="timeZoneDataTable">The TimeZone DataTable containign the schema to retrieve new rows from.</param> /// <param name="ruleDataTable">The Rule DataTable containign the schema to retrieve new rows from.</param> /// <param name="leapDataTable">The Leap DataTable containign the schema to retrieve new rows from.</param> /// <param name="directoryPath">The valid directory to parse Rules and TimeZones from.</param> /// <exception cref="System.Security.SecurityException">The caller does not have the required permission. </exception> /// <exception cref="System.ArgumentException">directoryPath contains invalid characters such as ", <, >, or |. </exception> /// <exception cref="System.IO.PathTooLongException">The specified path, file name, or both exceed the system-defined maximum length. /// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. /// The specified path, file name, or both are too long.</exception> public OlsonParser(System.Data.DataTable timeZoneDataTable, System.Data.DataTable ruleDataTable, System.Data.DataTable leapDataTable, OlsonDirectoryInfo files) { Directory = files; _olsonFiles = files.ValidOlsonFiles; _isoFile = files.IsoFile; _zoneFile = files.ZoneFile; ParsedEntries = 0; TimeZones = timeZoneDataTable; _timeZoneRows = TimeZones.Rows; Rules = ruleDataTable; _ruleRows = Rules.Rows; Leaps = leapDataTable; _leapRows = Leaps.Rows; _links = new List <string[]>(); TimeZoneLookupTable = new ConcurrentDictionary <string, System.Data.DataRow>(System.Environment.ProcessorCount, 450); _curYear = System.DateTime.Now.Year; // Pre-compiled regex for faster matching. Fields are separated from one another by any number of white space characters. _regex = new Regex(@"\s+", RegexOptions.Compiled); }
/// <summary> /// Instantiates and begins parsing the geonames and olson databases. /// </summary> public void BeginParsing() { RaiseParsingStarted(); ProgressText = "Verifying directories"; _olsonDirectoryInfo = new OlsonDirectoryInfo(Settings.Default.OlsonPath); bool isValidOlsonDir = _olsonDirectoryInfo.IsValid; _geonamesDirectoryInfo = new GeonamesDirectoryInfo(Settings.Default.GeonamesPath); bool isValidGeonamesDir = _geonamesDirectoryInfo.IsValid; if (!isValidOlsonDir && !isValidGeonamesDir) { // No need to lock, because only 1 thread accessing this. (UI Thread never accesses [Only creates it in app.xaml.cs]) App.ExceptionLogger.LogMedium(new InvalidDirectoryException("You must provide a valid Geonames directory, Olson directory, or both.")); RaiseParsingFinished(new ParsingFinishedEventArgs(ParsingResult.Failure)); return; } ProgressText = "Validating connection string"; using (SqlConnection conTest = new SqlConnection(ConnectionString)) { try { conTest.Open(); } catch (System.Exception e) { App.ExceptionLogger.LogMedium(e); RaiseParsingFinished(new ParsingFinishedEventArgs(ParsingResult.Failure)); return; } } _dbTools = new WorlTimeDatabaseTools(_conStrBuilder); TaskFactory f = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.None); Task stage1 = f.StartNew(() => PopulateFiles(isValidGeonamesDir, isValidOlsonDir)); Task stage3 = null; Task stage4 = null; Task stage5 = null; if (isValidOlsonDir) { _olsonParser = new OlsonParser(_dbTools.GetTimeZoneDataTable(), _dbTools.GetRuleDataTable(), _dbTools.GetLeapDataTable(), _olsonDirectoryInfo); _olsonParser.FileParsing += OnParserFileParsing; _olsonParser.FileParsed += (s, e) => ProgressValue++; _timeZoneLookUpTable = _olsonParser.TimeZoneLookupTable; ProgressText = "Creating Olson database tables"; _dbTools.CreateOlsonDatabaseTables(); Task stage2 = f.StartNew( () => { ProgressText = "Parsing Olson database files"; _olsonParser.ReadTZDirectory(); }); Task.WaitAll(stage2); stage3 = f.StartNew( () => { ProgressText = "Uploading TimeZones"; BulkCopy(_olsonParser.TimeZones, false); ProgressText = "TimeZones successfully uploaded"; }); stage4 = f.StartNew( () => { ProgressText = "Uploading Rules"; BulkCopy(_olsonParser.Rules, false); ProgressText = "Rules successfully uploaded"; }); stage5 = f.StartNew( () => { ProgressText = "Uploading Leaps"; BulkCopy(_olsonParser.Leaps, false); ProgressText = "Leaps succesfully uploaded"; }); } Task stage6 = null; if (isValidGeonamesDir) { DataTable cityTable = _dbTools.GetCityDataTable(); _geonamesParser = new GeonamesParser(_dbTools.GetFeatureCodeDataTable(), _geonamesDirectoryInfo); stage6 = f.StartNew( () => { ProgressMaximum++; ProgressText = "Parsing geonames featurecodes file"; DataTable featureCodes = _geonamesParser.ReadFeatureCodesFile(); ProgressText = "Uploading geonames featurecodes file"; BulkCopy(featureCodes); }); _geonamesParser.EntryParsed += (s, e) => { object[] cityValues = e.DatabaseEntry; DataRow timeZone; string timeZoneName = (string)cityValues[19]; if (_timeZoneLookUpTable.TryGetValue(timeZoneName, out timeZone)) { cityValues[18] = timeZone[0]; lock (_cityTableLock) { cityTable.Rows.Add(cityValues); } } else { App.ExceptionLogger.LogLow(new System.Exception("Could not get the ID for the City: " + (string)cityValues[1] + " with TimeZoneName " + timeZoneName)); } }; _geonamesParser.FileParsing += OnParserFileParsing; System.Action <FileParseEventArgs> BatchParsed = (e) => { string name = e.ParsedFile.Name; ProgressText = "Uploading File " + name; lock (_cityTableLock) { BulkCopy(cityTable); cityTable.Rows.Clear(); } ProgressText = name + " successfully uploaded"; }; _geonamesParser.FileParsed += (s, e) => BatchParsed(e); ProgressText = "Creating Geonames database table"; _dbTools.CreateGeonamesDatabaseTables(); ProgressText = "Parsing Geonames city files"; _geonamesParser.ReadCityFiles(); } Task.WaitAll(stage1); if (isValidOlsonDir) { Task.WaitAll(stage3, stage4, stage5); } if (isValidGeonamesDir) { Task.WaitAll(stage6); } RaiseParsingFinished(new ParsingFinishedEventArgs(ParsingResult.Success)); }