public Move(DateTime timestamp, PartialGisDataset oldDataset, PartialGisDataset?newDataset, PartialGisDataset?replacementDataset = null, string replacementLayerFilePath = null, string remarks = null) { Timestamp = timestamp; OldDataset = oldDataset; NewDataset = newDataset; ReplacementDataset = replacementDataset; ReplacementLayerFilePath = string.IsNullOrWhiteSpace(replacementLayerFilePath) ? null : replacementLayerFilePath; Remarks = string.IsNullOrWhiteSpace(remarks) ? null : remarks; }
private bool IsWorkspaceMatch(GisDataset dataset, PartialGisDataset moveFrom) { // Assumes workspace paths in moves do not have volume information string movePath = moveFrom.Workspace.Folder; string fullPath = dataset.Workspace.WithoutVolume; // TODO: The WorkspaceProgId from map data may have a ".1" (or other number?) suffix (everything in Theme Manager ends in '.1') if (moveFrom.WorkspaceProgId != null && moveFrom.WorkspaceProgId == dataset.WorkspaceProgId) { return(false); } return(string.Compare(fullPath, movePath, StringComparison.OrdinalIgnoreCase) == 0); }
private bool IsPartialWorkspaceMatch(GisDataset dataset, PartialGisDataset moveFrom) { // A partial workspace match (i.e. the current or an ancestral folder has moved) is only valid // if the moveFrom.DatasourceName is null; if not null, then we must do a DataSourceMatch if (moveFrom.DatasourceName != null) { return(false); } // Assumes workspace paths in moves do not have volume information string movePath = moveFrom.Workspace.Folder; string fullPath = dataset.Workspace.WithoutVolume; // Just searching for oldPath somewhere in newPath could yield some false positives. // Instead, strip the volume and only match the beginning of the string return(fullPath.StartsWith(movePath, StringComparison.OrdinalIgnoreCase)); }
private bool IsDataSourceMatch(GisDataset dataset, PartialGisDataset moveFrom) { if (dataset.DatasourceName == null) { return(false); } if (moveFrom.DatasourceName == null) { return(false); } if (String.Compare(dataset.DatasourceName, moveFrom.DatasourceName, StringComparison.OrdinalIgnoreCase) != 0) { return(false); } if (moveFrom.DatasourceType != null && moveFrom.DatasourceType != dataset.DatasourceType) { return(false); } return(IsWorkspaceMatch(dataset, moveFrom)); }
private PartialGisDataset?PatchDataset(GisDataset source, PartialGisDataset newDataset) { //A partial dataset must have a fully specified workspace, but a move may describe a parent folder //This method patches the new dataset by applying the move to the source workspace //The workspace in the move's old dataset must be a prefix of the input source for this method to return a non-null result var searchString = OldDataset.Workspace.Folder; int positionOfSearchString = source.Workspace.Folder.IndexOf(searchString, StringComparison.OrdinalIgnoreCase); if (positionOfSearchString < 0) { return(null); } var newWorkspace = source.Workspace.Folder.Substring(0, positionOfSearchString) + newDataset.Workspace.Folder + source.Workspace.Folder.Substring(positionOfSearchString + searchString.Length); return(new PartialGisDataset( newWorkspace, newDataset.WorkspaceProgId, newDataset.DatasourceName, newDataset.DatasourceType )); }
public Moves(string csvPath, char delimiter = '|', bool check = false) { const int fieldCount = 15; int lineNum = 0; //This is a very simple CSV parser, as the input is very simple. //The constructor is very forgiving on the input. It ignores any record which isn't valid. It doesn't throw any exceptions //It may contain an empty list of moves, which will is dealt with appropriately //The csv file used for input should be validated whenever it is edited. //Validation rules: // Each row must have 15 fields; row is split on delimiter ('|') which must not appear in a field. // Each row requires a "timestamp" in the first field, and the timestamp must be ordered from oldest to newest // There must be an old workspace path in the 2nd column. // Workspace paths must not have volume information // Column 3 if provided must be a valid esri WorkspaceFactoryProgId // Column 5 if provided must be a valid esriDatasetType // Replacement datasets are not supported - use replacement layer file // New and old datasets must not differ in workspace type or dataset type - use replacement layer file // If newDataset is null (i.e old is deleted), trash or archive, then a replacement layer file should be provided. // A remark is all that is mandatory when newDataset is null. // Column 2 and 6 must be empty or a valid esri WorkspaceFactoryProgId string. // Columns 4 and 8 must be empty or a valid esriDatasetType string. // workspace changes (i.e. dataSourceName is null) should be exclusive. // i.e. if there is a move /a/b => /x/y, we should not have /a/b/c => ... try { DateTime previousTimestamp = DateTime.MinValue; foreach (string line in File.ReadLines(csvPath)) { lineNum += 1; var row = line.Split(delimiter); if (row.Length != fieldCount) { if (check) { Console.WriteLine($"Warning: Wrong number of columns ({row.Length} not {fieldCount}) at line {lineNum}; Skipping."); } continue; } if (lineNum == 1 && row[0] == "timestamp") { // Skip a header row if present continue; } if (!DateTime.TryParse(row[0], out DateTime timestamp)) { if (check) { Console.WriteLine($"Warning: Value in column #1 ({row[0]}) at line {lineNum} is not a DateTime; Skipping."); } continue; } if (timestamp < previousTimestamp) { if (check) { Console.WriteLine($"Warning: Timestamp in column #1 must be increasing. Line {lineNum} is out of order; Skipping."); } continue; } previousTimestamp = timestamp; if (string.IsNullOrWhiteSpace(row[1])) { if (check) { Console.WriteLine($"Warning: No value provided for column #2 (Old Workspace Path) at line {lineNum}; Skipping."); } continue; } else if (!IsSimpleRelativePath(row[1])) { if (check) { Console.WriteLine($"Warning: Value in column #1 ({row[1]}) at line {lineNum} is not a simple relative path; Skipping."); } continue; } if (!string.IsNullOrWhiteSpace(row[4]) && !IsEsriDatasetType(row[4])) { if (check) { Console.WriteLine($"Warning: Value in column #5 ({row[4]}) at line {lineNum} is not an esriDatasetType; Skipping."); } continue; } if (!string.IsNullOrWhiteSpace(row[2]) && !IsWorkspaceFactoryProgId(row[2])) { if (check) { Console.WriteLine($"Warning: Value in column #3 ({row[2]}) at line {lineNum} is not an esriProgID; Skipping."); } continue; } var oldDataset = new PartialGisDataset(row[1], row[2], row[3], row[4]); PartialGisDataset?newDataset = null; if (!string.IsNullOrWhiteSpace(row[5])) { if (!IsSimpleRelativePath(row[5])) { if (check) { Console.WriteLine($"Warning: Value in column #6 ({row[5]}) at line {lineNum} is not a simple relative path; Skipping."); } continue; } if (!string.IsNullOrWhiteSpace(row[6]) && row[2] != row[6]) { if (check) { Console.WriteLine($"Warning: Column #7 does not match column #3 at line {lineNum} (new_workspace_type = {row[6]} <> old_workspace_type = {row[2]}); Skipping."); } continue; } // We do not need to check that row[6] is a progID, because it must be null or the same as row[2], which has already been checked. if (!string.IsNullOrWhiteSpace(row[8])) { if (!IsEsriDatasetType(row[8])) { if (check) { Console.WriteLine($"Warning: Value in column #9 ({row[8]}) at line {lineNum} is not an esriDatasetType; Skipping."); } continue; } if (row[4] != row[8]) { if (check) { Console.WriteLine($"Warning: Column #9 does not match column #5 at line {lineNum} (new_dataset_type = {row[8]} <> old_dataset_type = {row[4]}); Skipping."); } continue; } } newDataset = new PartialGisDataset(row[5], row[6], row[7], row[8]); } // replacement data source is not supported, so we ignore row[9] to row[12] if (check) { if (!string.IsNullOrWhiteSpace(row[9]) || !string.IsNullOrWhiteSpace(row[10]) || !string.IsNullOrWhiteSpace(row[11]) || !string.IsNullOrWhiteSpace(row[12])) { Console.WriteLine($"Warning: Values in columns 10 to 13 (Replacement datasets) at line {lineNum} are not supported; Using null for replacement dataset."); } } var layerFile = string.IsNullOrWhiteSpace(row[13]) ? null : row[13].Trim(); if (layerFile != null) { if (!IsSimilarToLayerFile(layerFile)) { if (check) { Console.WriteLine($"Warning: Value in column #14 ({layerFile}) at line {lineNum} is not a valid layer file; Skipping."); } continue; } } var remarks = string.IsNullOrWhiteSpace(row[14]) ? null : row[14].Trim(); if (newDataset == null && layerFile == null && remarks == null) { if (check) { Console.WriteLine($"Warning: Incomplete move at line {lineNum}. Neither a new dataset (column #6), nor a replacement layer file (column #14), nor a remark (column #15) was provided; Skipping."); } continue; } if (check) { if ((newDataset == null || newDataset.Value.Workspace.IsInTrash || newDataset.Value.Workspace.IsInArchive) && layerFile == null) { Console.WriteLine($"Warning: A value in column #14 (replacement_layerFile_path) at line {lineNum} is STRONGLY encouraged when column #5-8 (new_dataset) is null or in the Trash/Archive."); } } _moves.Add(new Move(timestamp, oldDataset, newDataset, null, layerFile, remarks)); } if (check) { Console.WriteLine($"Scanned {lineNum} lines. Found {_moves.Count} moves."); Console.WriteLine("Checking consistency of moves"); ConsistencyCheck(_moves); Console.WriteLine("Done."); } } catch (Exception e) { if (check) { Console.WriteLine($"Aborting due to error at line {lineNum}: {e.Message}."); } } }