/* ========================================================================
* Copyright 2018 - Volian Enterprises, Inc. All rights reserved.
* Volian Enterprises - Proprietary Information - DO NOT COPY OR DISTRIBUTE
* ------------------------------------------------------------------------
* This program is to automate the Comparison of PDF output using the debug
* files created during automated testing. The SQL script is used to create
* the debug files; DebugPagination.txt and DebugMeta.txt. This comparison
* code then compares these files run with two versions of the PROMS code
* and finds pages that have been impacted by the changes to the PROMS code.
*
* By Identifying Procedures and Pages that have been changed the amount of
* time required to perform comparisons is greatly reduced.
*
* The Pagination Comparison simply looks for things that have impacted page
* breaks. This is a quick summary of the page breaks for all of the
* procedures in a folder. Thus, the folders are listed which contain
* differences. When a folder is selected, the pagination information is
* compared for all of the procedures in each folder. If no changes exits,
* then the folder is not contained in the list. after selecting a folder,
* a comparison is shown of the pagination lines that are different.
* Pressing the UC button allows you to see ultracompare with the two
* pagination files. If you select a line that is different, two pdf
* windows are opened to the relative pages.
*
* The Meta Comparison looks at more detail including text and formatting
* differences. This information is organized by procedure and page. It
* uses the procedure/page/line classes. The structure of this file is
* described in
* V:\Proms Versions\Automated Testing\Baseline Metafile Key.docx
* By Clicking on a folder a list of impacted procedures is provided.
* By clicking on a Procedure a list of pages and lines are provided.
* By clicking on a line the PDF is displayed.
*
* The Search feature performs a text search on the Meta File. Looking
* for RCP will tell you where RCP can be found in the PDFs. This may be
* of use if you want to see where certain text is contained within all of
* the PDFs. Looking for [[ will find all of the special characters which
* have not yet been handled in the PROMS meta code. See FixText in
* DisplayText.cs in the PROMS code.
* ======================================================================*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Collections.Specialized;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Xml;
using System.Runtime.InteropServices;
namespace Baseline
{
enum LastWas
{
Pagination,
Baseline,
Search
};
public enum Relation
{
Contains,
StartsWith,
EndsWith,
Regex
}
public partial class frmBaseline : Form
{
private IgnoreLines _MyIgnore = new IgnoreLines();
public IgnoreLines MyIgnore
{
get { return _MyIgnore; }
set { _MyIgnore = value; }
}
private LastWas myLast = LastWas.Search;
private Settings MySettings;
public string MyStatus
{
get { return tsslStatus.Text; }
set
{
tsslStatus.Text = value;
Application.DoEvents();
}
}
public frmBaseline()
{
InitializeComponent();
LoadSettings();
SetupEventHandlers();
MyStatus = "Ready";
}
#region Settings
///
/// Setup event handlers for changes to Settings
///
private void SetupEventHandlers()
{
this.Resize += frmBaseline_Resize;
this.Move += frmBaseline_Move;
this.splitContainer1.SplitterMoved += splitContainer1_SplitterMoved;
this.splitContainer2.SplitterMoved += splitContainer2_SplitterMoved;
this.splitContainer3.SplitterMoved += splitContainer3_SplitterMoved;
cbFile1.TextChanged += cbFile1_TextChanged;
cbFile2.TextChanged += cbFile2_TextChanged;
}
// Remember the location of splitters
void splitContainer3_SplitterMoved(object sender, SplitterEventArgs e)
{
Properties.Settings.Default.Split3 = splitContainer3.SplitterDistance;
Properties.Settings.Default.Save();
}
void splitContainer2_SplitterMoved(object sender, SplitterEventArgs e)
{
Properties.Settings.Default.Split2 = splitContainer2.SplitterDistance;
Properties.Settings.Default.Save();
}
void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e)
{
Properties.Settings.Default.Split1 = splitContainer1.SplitterDistance;
Properties.Settings.Default.Save();
}
// Remember Form Location
void frmBaseline_Move(object sender, EventArgs e)
{
Properties.Settings.Default.Location = this.Location;
Properties.Settings.Default.Save();
}
// Remember Form Size
void frmBaseline_Resize(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Normal)
{
Properties.Settings.Default.Size = this.Size;
}
Properties.Settings.Default.WidnowState = this.WindowState;
Properties.Settings.Default.Save();
}
// Load Settings
private void LoadSettings()
{
this.Location = Properties.Settings.Default.Location;
this.Size = Properties.Settings.Default.Size;
this.WindowState = Properties.Settings.Default.WidnowState;
if(Properties.Settings.Default.Ignore != null && Properties.Settings.Default.Ignore != "")
MyIgnore = IgnoreLines.Get(Properties.Settings.Default.Ignore);
MySettings= new Settings();
MySettings.IgnoreLines = new BindingList();
splitContainer1.SplitterDistance = Properties.Settings.Default.Split1;
splitContainer2.SplitterDistance = Properties.Settings.Default.Split2;
splitContainer3.SplitterDistance = Properties.Settings.Default.Split3;
if (Properties.Settings.Default.MRU1 != null && Properties.Settings.Default.MRU1.Count > 0)
{
cbFile1.Items.Clear();
foreach (string str in Properties.Settings.Default.MRU1)
cbFile1.Items.Add(str);
cbFile1.SelectedIndex = 0;
}
if (Properties.Settings.Default.MRU2 != null && Properties.Settings.Default.MRU2.Count > 0)
{
cbFile2.Items.Clear();
foreach (string str in Properties.Settings.Default.MRU2)
cbFile2.Items.Add(str);
cbFile2.SelectedIndex = 0;
}
}
#endregion
///
/// Use FolderBrowserDialog to find folder
///
///
///
private void btnBrowse1_Click(object sender, EventArgs e)
{
fbd.SelectedPath = cbFile1.Text;
if (fbd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
cbFile1.Text = fbd.SelectedPath;
}
private void btnBrowse2_Click(object sender, EventArgs e)
{
fbd.SelectedPath = cbFile2.Text;
if (fbd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
cbFile2.Text = fbd.SelectedPath;
}
///
/// Open frmSettings to edit ignore list
///
///
///
private void btnSettings_Click(object sender, EventArgs e)
{
string saveOriginal = MyIgnore.ToString();
frmSettings mySettings = new frmSettings(MyIgnore);
if (mySettings.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
Properties.Settings.Default.Ignore = MyIgnore.ToString();
Properties.Settings.Default.Save();
}
else
{
MyIgnore = IgnoreLines.Get(saveOriginal);
}
}
///
/// Process DebugPagination.txt in all sub-folders
///
///
///
private void btnPagination_Click(object sender, EventArgs e)
{
myLast = LastWas.Pagination;
// Initialize lbDifferent DocVersion comparison list
lbDifferent.DataSource = null;
lbDifferent.Items.Clear();
// Initialize line differences for DebugPagination
lbResults1.Items.Clear();
lbResults2.Items.Clear();
MyStatus = "Searching...";
// Perform DebugPagination Comparison
FindFiles fnd = new FindFiles(cbFile1.Text, cbFile2.Text, "DebugPagination.txt",MyIgnore);
lbDifferent.DataSource = fnd;
lbDifferent.DisplayMember = "File1";
MyStatus = string.Format("{0} Differences Found", fnd.Count);
}
///
/// Fill Appropriate List Boxes when an entry is selected in lbDifferent
///
///
///
private void lbDifferent_SelectedIndexChanged(object sender, EventArgs e)
{
//Initialize ListBoxes
lbProcedures.Items.Clear();
lbResults1.Items.Clear();
lbResults2.Items.Clear();
FindFile ff = lbDifferent.SelectedItem as FindFile;
if (ff != null)
{
// Fill Procedure or Result ListBoxes
switch (myLast)
{
case LastWas.Pagination:
CompareContent(ff.File1, ff.File2);// Compare DebugPagination
break;
case LastWas.Baseline:
CompareContent3(ff.File1, ff.File2);// Compare DebugMeta
break;
case LastWas.Search:
ShowSearchResults(ff.File1, ff.File2);// Perform search on DebugMeta
break;
default:
CompareContent(ff.File1, ff.File2);//Default DebugPagination
break;
}
//CompareOneFile(ff.File1, ff.File2);
}
}
Procedures MyProcs1;
Procedures MyProcs2;
///
/// Use LINQ to find a search string in the DebugMeta Files
///
///
///
private void ShowSearchResults(string file1, string file2)
{
IEnumerable lines1 = File.ReadLines(file1);// LINQ Read Lines
IEnumerable lines2 = File.ReadLines(file2);// LINQ Read Lines
lines1 = AddItemIDs(lines1);// LINQ Add ItemIDs to each line to make them unique
lines2 = AddItemIDs(lines2);// LINQ Add ItemIDs to each line to make them unique
// LINQ Lambda Expression - Only includes lines that contain the search text
// Each line x such that local function Contains(tbsearch.Text, x) is true
lines1 = lines1.Where(x => Contains(tbSearch.Text, x));// LINQ Search
lines2 = lines2.Where(x => Contains(tbSearch.Text, x));// LINQ Search
// Initialize Results
lbResults1.Items.Clear();
lbResults2.Items.Clear();
// Initialize Procedure List
lbProcedures.Items.Clear();
// TODO: Are the following lines necessary
string line1 = lines1.ElementAt(0);
string line2 = lines2.ElementAt(0);
// Load a List of Procedures
MyProcs1 = FillProcedures(lines1);
MyProcs2 = FillProcedures(lines2);
// Populate the Procedure ListBox
foreach (Procedure proc in MyProcs1)
{
// TODO: Is the following local variable necessary
int i = lbProcedures.Items.Add(proc);
}
MyStatus = string.Format("{0} Procedures contain {1}",MyProcs1.Count(),tbSearch.Text);
}
///
/// Fill the Procedure ListBox
///
///
///
private Procedures FillProcedures(IEnumerable lines)
{
Procedures myProcs = new Procedures();
string lastProc = null;
int pageNumber = 0;
foreach (string line in lines)
{
if (line.StartsWith("!!")) // !! Lines contain Procedure Info
{
lastProc = line;
//Remember last Procedure line
pageNumber = 1;
}
else if (line.StartsWith("Page Change from ")) // These lines contain Page Number
{
pageNumber = int.Parse(line.Substring(line.LastIndexOf(' ')));
// Remember last page number
}
else
{
// use remembered procedure and page number to save line of text
myProcs.Add(lastProc, pageNumber, line);// Otherwise add Procedure, Page or Line
}
}
return myProcs;
}
///
/// LINQ add ItemID Prefix to each line
///
///
///
private static IEnumerable AddItemIDs(IEnumerable lines1)
{
List list1 = new List();
string prefix = "";
foreach (string line in lines1)
{
if (line.StartsWith("TX ")) //Get the ItemID
{
if (line.Contains("ItmID="))
{
prefix = line.Substring(line.IndexOf("ItmID=") + 6);
}
else
prefix = "";
list1.Add(line);
}
else if (line.Contains(" Atrbs: "))// Add the prefix to make the line unique
list1.Add(prefix + line);
else
{
prefix = "";
list1.Add(line);
}
}
lines1 = list1.AsEnumerable();// Convert back to Enumerable to work with LINQ
return lines1;
}
private string GetProcNum(string line)
{
string retval = line.Substring(3, line.IndexOf(" | ") - 3);
if (retval.Contains("_"))
retval = retval.Substring(0, retval.IndexOf("_") - 1);
return retval;
}
///
/// Include lines for Procedure or Page or Search is true
/// Account for Case Insensitive CheckBox
///
///
///
///
private bool Contains(string searchText, string x)
{
bool result;
if (cbCaseSensitive.Checked)
{
result = x.Contains(searchText) || x.StartsWith("Page Change from ") || Regex.IsMatch(x, @"^!![^|]+\|[^|]+?$", RegexOptions.Compiled);
}
else
{
result = x.ToUpper().Contains(searchText.ToUpper()) || x.StartsWith("Page Change from ") || Regex.IsMatch(x, @"^!![^|]+\|[^|]+?$", RegexOptions.Compiled);
}
return result;
}
private void CompareContent(string file1, string file2)
{
// Enable the UltraCompare button
// Collapse the Procedure Panel
btnUC.Enabled = splitContainer3.Panel2Collapsed = true;
IEnumerable lines1 = FindFiles.ReadFilteredLines(file1, true, MyIgnore);
IEnumerable lines2 = FindFiles.ReadFilteredLines(file2, true, MyIgnore);
IEnumerable missing1 = lines1.Except(lines2);
IEnumerable missing2 = lines2.Except(lines1);
lbResults1.Items.Clear();
lbResults1.Items.AddRange(missing1.ToArray());
lbResults2.Items.Clear();
lbResults2.Items.AddRange(missing2.ToArray());
}
///
/// LINQ Perform DebugMeta Comparison
///
///
///
private void CompareContent3(string file1, string file2)
{
// Disble the UltraCompare button
// Expand the Procedure Panel
btnUC.Enabled = splitContainer3.Panel2Collapsed = false;
IEnumerable lines1 = FindFiles.ReadFilteredLines(file1, true,MyIgnore);// LINQ Read lines excluding lines to be ignored
IEnumerable lines2 = FindFiles.ReadFilteredLines(file2, true, MyIgnore);// LINQ Read lines excluding lines to be ignored
lines1 = AddItemIDs(lines1);// LINQ Add ItemID prefix to make lines unique
lines2 = AddItemIDs(lines2);// LINQ Add ItemID prefix to make lines unique
// The following lines create a list of lines without Page Numbers or procedures
// They are removed from this list so that they will not be impacted by the intersect below
IEnumerable noPageNumbers1 = lines1.Where(x => x.StartsWith("Page Change from ") == false);// LINQ get lines without Page Numbers
IEnumerable noPageNumbers2 = lines2.Where(x => x.StartsWith("Page Change from ") == false);// LINQ get lines without Page Numbers
noPageNumbers1 = noPageNumbers1.Where(x => x.StartsWith("!! ") == false);//LINQ remove lines with !!
noPageNumbers2 = noPageNumbers2.Where(x => x.StartsWith("!! ") == false);//LINQ remove lines with !!
IEnumerable intersect = noPageNumbers1.Intersect(noPageNumbers2);//LINQ find Matchhing lines
List lIntersect = intersect.ToList();//LINQ Create a list of matching lines
HashSet hs = new HashSet(intersect);// LINQ Create a HashSet of matching lines
IEnumerable missing1 = lines1.Where(x => !hs.Contains(x));// LINQ Remove matching lines
IEnumerable missing2 = lines2.Where(x => !hs.Contains(x));// LINQ Remove matching lines
MyProcs1 = FillProcedures(missing1);// Fill Procedures from the lines that don't match (including Procedure and Page Number lines)
MyProcs2 = FillProcedures(missing2);// Fill Procedures from the lines that don't match (including Procedure and Page Number lines)
if (MyProcs1 != null && MyProcs1.Count > 0)// Fill Procedure ListBox with MyProcs1
{
foreach (Procedure proc in MyProcs1)
lbProcedures.Items.Add(proc);
MyStatus = string.Format("{0} Procedures found with differences", MyProcs1.Count());
}
else if (MyProcs2 != null && MyProcs2.Count > 0)// Fill Procedure ListBox with MyProcs1
{
foreach (Procedure proc in MyProcs2)
lbProcedures.Items.Add(proc);
MyStatus = string.Format("{0} Procedures found with differences", MyProcs2.Count());
}
}
///
/// Open One or more PDF's
///
///
///
private void lbResults1_SelectedIndexChanged(object sender, EventArgs e)
{
string line=null;
if (lbResults1.SelectedItem is string)
line = (string)lbResults1.SelectedItem;
Line myLine = lbResults1.SelectedItem as Line;
switch (myLast)
{
case LastWas.Pagination:
line = OpenPDF(line);
break;
case LastWas.Baseline: // TODO: Need to add code here to open matching file
OpenOnePDF(myLine,1);
break;
case LastWas.Search: // TODO: Need to add code here to open matching file
OpenOnePDF(myLine,1);
break;
default:
break;
}
}
private void lbResults2_SelectedIndexChanged(object sender, EventArgs e)
{
string line=null;
if(lbResults2.SelectedItem is string)
line = (string)lbResults2.SelectedItem;
Line myLine = lbResults2.SelectedItem as Line;
switch (myLast)
{
case LastWas.Pagination:
line = OpenPDF(line);
break;
case LastWas.Baseline: // TODO: Need to add code here to open matching file
OpenOnePDF(myLine,2);
break;
case LastWas.Search: // TODO: Need to add code here to open matching file
OpenOnePDF(myLine,2);
break;
default:
break;
}
}
string exePath;
private string OpenPDF(string line)
{
int page = int.Parse(line.Substring(0, 6));
// B2018-113 - Replace slashes and backslashes with underscores just as PROMS does when creating a PDF file.
line = line.Substring(8, line.IndexOf(".S") - 8).Replace("/", "_").Replace("\\", "_");
FindFile ff = lbDifferent.SelectedItem as FindFile;
FileInfo fi1 = new FileInfo(ff.File1);
FileInfo fi2 = new FileInfo(ff.File2);
// If you don't know where the Reader executable is for PDFs Open a PDF and Check to see where the path points
if (exePath == null)
{
System.Diagnostics.Process p = System.Diagnostics.Process.Start(fi1.DirectoryName + "\\" + line + ".pdf");
exePath = TryToGetPath(p);
p.Kill(); // No need to keep it open
}
// Open the first PDF on a Specific Page
System.Diagnostics.ProcessStartInfo psi1 = new System.Diagnostics.ProcessStartInfo(exePath, string.Format("/A page={0} ", page) + fi1.DirectoryName + "\\" + line + ".pdf ");
System.Diagnostics.Process p1 = System.Diagnostics.Process.Start(psi1);
// Move the PDF Reader window to 0,0
MoveProcess(p1, 0, 0);
// Open the first PDF on a Specific Page
System.Diagnostics.ProcessStartInfo psi2 = new System.Diagnostics.ProcessStartInfo(exePath, string.Format("/A page={0} ", page) + fi2.DirectoryName + "\\" + line + ".pdf ");
System.Diagnostics.Process p2 = System.Diagnostics.Process.Start(psi2);
// Move the PDF Reader window to 960,0
// TODO: This Offset could be a Setting
MoveProcess(p2, 960, 0);
return line;
}
///
/// Try to get the location of the PDF Reader executable
///
/// Process of PDF Reader
///
private string TryToGetPath(System.Diagnostics.Process p)
{
p.WaitForInputIdle();
while (p.MainModule == null)
{
Console.WriteLine("{0} - {1}", p.MainWindowTitle,p.ProcessName);
p.WaitForInputIdle();
Application.DoEvents();
}
return p.MainModule.FileName;
}
///
/// Start UltraCompare of two files
///
///
///
private void CompareOneFile(string compareFile, string baseFile)
{
//Console.WriteLine("Compare {0} and {1}", compareFile, baseFile);
string progname = string.Empty;
if (System.IO.File.Exists(@"C:\Program Files\IDM Computer Solutions\UltraCompare\UC.exe"))
progname = @"C:\Program Files\IDM Computer Solutions\UltraCompare\UC.exe";
if (System.IO.File.Exists(@"C:\Program Files (x86)\IDM Computer Solutions\UltraCompare\UC.exe"))
progname = @"C:\Program Files (x86)\IDM Computer Solutions\UltraCompare\UC.exe";
System.Diagnostics.ProcessStartInfo psi =
new System.Diagnostics.ProcessStartInfo(progname, string.Format(@" -t ""{0}"" ""{1}""", compareFile, baseFile));
System.Diagnostics.Process prc = System.Diagnostics.Process.Start(psi);
}
private ProcessLocationQueue myQueue= new ProcessLocationQueue();
private Timer queueTimer = null;
///
/// Move a Process to a specific screen location - This is done with a timer so
/// that it waits until windows are finished being initialized.
///
///
///
///
private void MoveProcess(System.Diagnostics.Process proc, int x, int y)
{
if (queueTimer == null)
{
queueTimer = new Timer();
queueTimer.Enabled = false;
queueTimer.Tick += queueTimer_Tick;
queueTimer.Interval = 1000;
}
myQueue.Add(proc, x, y);
if (!queueTimer.Enabled)
queueTimer.Enabled = true;
}
///
/// timer tick for moving a process window
///
///
///
void queueTimer_Tick(object sender, EventArgs e)
{
while (myQueue.Count > 0)
myQueue.ProcessNext();
queueTimer.Enabled = false;
}
///
/// Open PDF associated with the selected line
///
///
///
private void OpenOnePDF(Line myLine, int list)
{
// B2018-113 - Replace slashes and backslashes with underscores just as PROMS does when creating a PDF file.
string proc = myLine.MyProc.Number.Replace("/","_").Replace("\\","_");
int pagenum = myLine.MyPage.Number;
FindFile ff = lbDifferent.SelectedItem as FindFile;
FileInfo fi1 = new FileInfo(ff.File1);
FileInfo fi2 = new FileInfo(ff.File2);
if (exePath == null)
{
System.Diagnostics.Process p = System.Diagnostics.Process.Start(fi1.DirectoryName + "\\" + proc + ".pdf");
while (exePath == null)
{
try
{
exePath = p.MainModule.FileName;
}
catch (Exception ex)
{
Application.DoEvents();
Console.WriteLine("{0} - {1}", ex.GetType().Name, ex.Message);
}
}
p.Kill();
}
if (list == 1)
{
System.Diagnostics.ProcessStartInfo psi1 = new System.Diagnostics.ProcessStartInfo(exePath, string.Format("/A page={0} ", pagenum) + fi1.DirectoryName + "\\" + proc + ".pdf ");
System.Diagnostics.Process p1 = System.Diagnostics.Process.Start(psi1);
}
else
{
System.Diagnostics.ProcessStartInfo psi2 = new System.Diagnostics.ProcessStartInfo(exePath, string.Format("/A page={0} ", pagenum) + fi2.DirectoryName + "\\" + proc + ".pdf ");
System.Diagnostics.Process p1 = System.Diagnostics.Process.Start(psi2);
}
}
///
/// Perform Debug Meta file comparison for all of the folders within the automated testing folders
///
///
///
private void btnBaseline_Click(object sender, EventArgs e)
{
myLast = LastWas.Baseline;
lbDifferent.DataSource = null;
// Initialize Differnce and Results ListBoxes
lbDifferent.Items.Clear();
lbResults1.Items.Clear();
lbResults2.Items.Clear();
MyStatus = "Searching...";
// Perform code to find DebugMeta files with diffences
FindFiles fnd = new FindFiles(cbFile1.Text, cbFile2.Text, "DebugMeta.txt",MyIgnore);
lbDifferent.DataSource = fnd;
lbDifferent.DisplayMember = "File1";
MyStatus = string.Format("{0} Differences Found", fnd.Count);
}
///
/// Perform search of all the DocVersions Debug.Meta Files
///
///
///
private void btnSearch_Click(object sender, EventArgs e)
{
// Disble the UltraCompare button
// Expand the Procedure Panel
btnUC.Enabled= splitContainer3.Panel2Collapsed = false;
myLast = LastWas.Search;
// Initialize ListBoxes
lbDifferent.DataSource = null;
lbDifferent.Items.Clear();
lbResults1.Items.Clear();
lbResults2.Items.Clear();
MyStatus = "Searching...";
//Perform Search
FindFiles fnd = new FindFiles(cbFile1.Text, cbFile2.Text, "DebugMeta.txt", tbSearch.Text, SearchType.Contains, cbCaseSensitive.Checked);
lbDifferent.DataSource = fnd;
lbDifferent.DisplayMember = "File1";
MyStatus = string.Format("{0} Differences Found", fnd.Count);
}
bool InHandler1 = false;
// Track changes to the File Path 1 including Most Recent Used
private void cbFile1_TextChanged(object sender, EventArgs e)
{
if (InHandler1) return;
InHandler1 = true;
string str = cbFile1.Text;
while (cbFile1.Items.Contains(str))
cbFile1.Items.Remove(str);
cbFile1.Items.Insert(0, str);
cbFile1.SelectedIndex = 0;
Properties.Settings.Default.MRU1 = new System.Collections.Specialized.StringCollection();
foreach (string str1 in cbFile1.Items)
Properties.Settings.Default.MRU1.Add(str1);
Properties.Settings.Default.Save();
InHandler1 = false;
}
bool InHandler2 = false;
// Track changes to the File Path 2 including Most Recent Used
private void cbFile2_TextChanged(object sender, EventArgs e)
{
if (InHandler2) return;
InHandler2 = true;
string str = cbFile2.Text;
while (cbFile2.Items.Contains(str))
cbFile2.Items.Remove(str);
cbFile2.Items.Insert(0, str);
cbFile2.SelectedIndex = 0;
Properties.Settings.Default.MRU2 = new System.Collections.Specialized.StringCollection();
foreach (string str2 in cbFile2.Items)
Properties.Settings.Default.MRU2.Add(str2);
Properties.Settings.Default.Save();
InHandler2 = false;
}
private void lbProcedures_SelectedIndexChanged(object sender, EventArgs e)
{
//Initialize Results List Box
lbResults1.Items.Clear();
Procedure myProc = lbProcedures.SelectedItem as Procedure;
//TODO: May need to consider if there are duplicate procedure numers and titles
Procedure myProc1 = MyProcs1.Find(x => x.Number == myProc.Number && x.Title == myProc.Title);
// Build the results ListBox for the left window
if (myProc1 != null)
{
foreach (Page myPage in myProc1.MyPages)
{
lbResults1.Items.Add(myPage);
foreach (Line myLine in myPage.MyLines)
lbResults1.Items.Add(myLine);
}
}
lbResults2.Items.Clear();
// Build the results ListBox for the right window
Procedure myProc2 = MyProcs2.Find(x => x.Number == myProc.Number && x.Title == myProc.Title);
if (myProc2 != null)
{
foreach (Page myPage in myProc2.MyPages)
{
lbResults2.Items.Add(myPage);
foreach (Line myLine in myPage.MyLines)
lbResults2.Items.Add(myLine);
}
}
}
///
/// Open Ultra Compare for the currently selected DocVersion
///
///
///
private void btnUC_Click(object sender, EventArgs e)
{
if (myLast == LastWas.Pagination)
{
FindFile ff = lbDifferent.SelectedItem as FindFile;
CompareOneFile(ff.File1, ff.File2);
}
}
}
public enum SearchType
{
Contains,
StartsWith,
EndsWith
};
public class Settings
{
private BindingList _IgnoreLines;
public BindingList IgnoreLines
{
get { return _IgnoreLines; }
set { _IgnoreLines = value; }
}
}
public partial class FindFile
{
private string _File1;
public string File1
{
get { return _File1; }
set { _File1 = value; }
}
private string _File2;
public string File2
{
get { return _File2; }
set { _File2 = value; }
}
public FindFile(string file1, string file2)
{
File1 = file1;
File2 = file2;
}
}
public partial class FindFiles : List
{
private string _FileName;
public string FileName
{
get { return _FileName; }
}
///
/// Build list of DocVersion Folders with differences
///
/// Base path
/// Compare path
/// filename
/// Ignore list
public FindFiles(string path1, string path2, string fileName,IgnoreLines myIgnore)
{
DirectoryInfo di1 = new DirectoryInfo(path1);
DirectoryInfo di2 = new DirectoryInfo(path2);
_FileName = fileName;
FillByCompare(di1, di2, fileName, myIgnore);
}
///
/// Fill for a search
///
///
///
///
///
///
///
public FindFiles(string path1, string path2, string fileName, string searchText, SearchType searchType, bool caseSensitive)
{
DirectoryInfo di1 = new DirectoryInfo(path1);
DirectoryInfo di2 = new DirectoryInfo(path2);
_FileName = fileName;
FillByCompare(di1, di2, fileName,searchText,searchType, caseSensitive);
}
///
/// Fill results by search
///
///
///
///
///
///
///
private void FillByCompare(DirectoryInfo di1, DirectoryInfo di2, string fileName, string searchText, SearchType searchType, bool caseSensitive)
{
foreach (DirectoryInfo diChild1 in di1.GetDirectories())
{
DirectoryInfo diChild2 = new DirectoryInfo(di2.FullName + "\\" + diChild1.Name);
if (diChild2.Exists)
FillByCompare(diChild1, diChild2, fileName,searchText,searchType,caseSensitive);
}
foreach (FileInfo fiChild1 in di1.GetFiles(fileName))
{
FileInfo fiChild2 = new FileInfo(di2.FullName + "\\" + fiChild1.Name);
if (fiChild2.Exists)
{
if (CompareFile(fiChild1, fiChild2,searchText,searchType, caseSensitive))
Add(new FindFile(fiChild1.FullName, fiChild2.FullName));
}
}
}
///
/// LINQ Peform Searc on DebugMeta files in all DocVersion folders
///
///
///
///
///
///
///
private bool CompareFile(FileInfo fiChild1, FileInfo fiChild2, string searchText, SearchType searchType,bool caseSensitive)
{
IEnumerable lines1=null;
IEnumerable lines2=null;
switch (searchType)
{
case SearchType.Contains:
if (caseSensitive)
{
lines1 = File.ReadLines(fiChild1.FullName).Where(x => x.Contains(searchText));
lines2 = File.ReadLines(fiChild2.FullName).Where(x => x.Contains(searchText));
}
else
{
lines1 = File.ReadLines(fiChild1.FullName).Where(x => x.ToUpper().Contains(searchText.ToUpper()));
lines2 = File.ReadLines(fiChild2.FullName).Where(x => x.ToUpper().Contains(searchText.ToUpper()));
}
break;
case SearchType.StartsWith:
if (caseSensitive)
{
lines1 = File.ReadLines(fiChild1.FullName).Where(x => x.StartsWith(searchText));
lines2 = File.ReadLines(fiChild2.FullName).Where(x => x.StartsWith(searchText));
}
else
{
lines1 = File.ReadLines(fiChild1.FullName).Where(x => x.ToUpper().StartsWith(searchText.ToUpper()));
lines2 = File.ReadLines(fiChild2.FullName).Where(x => x.ToUpper().StartsWith(searchText.ToUpper()));
}
break;
case SearchType.EndsWith:
if (caseSensitive)
{
lines1 = File.ReadLines(fiChild1.FullName).Where(x => x.EndsWith(searchText));
lines2 = File.ReadLines(fiChild2.FullName).Where(x => x.EndsWith(searchText));
}
else
{
lines1 = File.ReadLines(fiChild1.FullName).Where(x => x.ToUpper().EndsWith(searchText.ToUpper()));
lines2 = File.ReadLines(fiChild2.FullName).Where(x => x.ToUpper().EndsWith(searchText.ToUpper()));
}
break;
}
if (lines1 != null && lines2 != null)
{
if ((lines1.Count() > 0) || (lines2.Count() > 0))
{
return true;
}
return false;
}
return false;
}
private void FillByCompare(DirectoryInfo di1, DirectoryInfo di2, string fileName, IgnoreLines myIgnore)
{
foreach (DirectoryInfo diChild1 in di1.GetDirectories())
{
DirectoryInfo diChild2 = new DirectoryInfo(di2.FullName + "\\" + diChild1.Name);
if (diChild2.Exists)
FillByCompare(diChild1, diChild2, fileName,myIgnore);// Recursively work on Sub-Folders
}
foreach (FileInfo fiChild1 in di1.GetFiles(fileName))
{
FileInfo fiChild2 = new FileInfo(di2.FullName + "\\" + fiChild1.Name);
if (fiChild2.Exists)
{
if (CompareFile(fiChild1, fiChild2,myIgnore))
Add(new FindFile(fiChild1.FullName, fiChild2.FullName));// Process Each File
}
}
}
private bool CompareFile(FileInfo fiChild1, FileInfo fiChild2,IgnoreLines myIgnore)
{
if (fiChild1.Name == "DebugMeta.txt")
{
IEnumerable lines1 = ReadFilteredLines(fiChild1.FullName, false, myIgnore);//LINQ Read lines without ignored lines
IEnumerable lines2 = ReadFilteredLines(fiChild2.FullName, false, myIgnore);//LINQ Read lines without ignored lines
if (lines1.Except(lines2).Count() == 0 &&
lines2.Except(lines1).Count() == 0) //Look for lines in one file that are not in the other
return false;
return true;
}
else
{
string file1Contents = ReadContents(fiChild1);// This is just a simple text comparison
string file2Contents = ReadContents(fiChild2);
return !file1Contents.Equals(file2Contents);
}
}
///
/// LINQ - Readlines and remove items based upon the ignore list
///
///
///
///
///
public static IEnumerable ReadFilteredLines(string child1, bool includeProcedures, IgnoreLines myIgnore)
{
IEnumerable lines = File.ReadLines(child1);
lines = lines.Where(x => FindProcedure(x,includeProcedures));
foreach (IgnoreLine ignore in myIgnore)
{
if (ignore.Active)
{
switch (ignore.SearchType)
{
case Relation.Contains:
lines = lines.Where(x => x.Contains(ignore.Text) == false);
break;
case Relation.StartsWith:
lines = lines.Where(x => x.StartsWith(ignore.Text) == false);
break;
case Relation.EndsWith:
lines = lines.Where(x => x.EndsWith(ignore.Text) == false);
break;
case Relation.Regex:
Regex myreg = new Regex(ignore.Text, RegexOptions.Compiled);
lines = lines.Where(x => myreg.IsMatch(x) == false);
break;
default:
break;
}
}
}
return lines;
}
///
/// Find a procedure line in a meta file
///
///
///
///
private static bool FindProcedure(string x, bool includeProcedures)
{
bool result = x.StartsWith("!!") == false;
if(includeProcedures) result |= Regex.IsMatch(x, @"^!![^|]+\|[^|]+?$", RegexOptions.Compiled);
return result;
}
///
/// basic text read
///
///
///
private string ReadContents(FileInfo fiChild1)
{
StreamReader sr = fiChild1.OpenText();
string buff = sr.ReadToEnd();
return buff;
}
}
// Classes for structured data storage used for meta file comparisons
//
// Procedure contains:
// Number - Procedure Number
// Title - Procedure Title
// MyPages - The Page objects associated with this procedure
//
// Page contains:
// Number - PageNumber
// MyLines - The Line Objects associated with this Page
//
// Line contains:
// MyProc - The containing procedure
// MyPage - the containing page
// Text - the line of text
public partial class Procedure
{
private string _Number;
public string Number
{
get { return _Number; }
set { _Number = value; }
}
private string _Title;
public string Title
{
get { return _Title; }
set { _Title = value; }
}
private Pages _MyPages = new Pages();
public Pages MyPages
{
get { return _MyPages; }
set { _MyPages = value; }
}
public Procedure(string number, string title)
{
_Number = number;
_Title = title;
}
public override string ToString()
{
return string.Format("{0} - {1}", Number, Title);
}
}
public partial class Procedures : List
{
// Sample data for a Procedure Number line
// !! E-0 | Reactor Trip Or Safety Injection
// The First Group is the Procedure Number
// The Second Group is the Procedure Title
public Regex parseLine = new Regex("^!! (.*) \\| (.*)$", RegexOptions.Compiled);
///
/// This adds a procedure, page and line class as needed, If the procedure
/// already exists it is used. If the page already exists it is used.
///
/// Procedure Line matches Regular Expression above
/// Page Number
/// Text from the meta File
public void Add(string lastProc, int pageNumber, string text)
{
if (lastProc == null) return;
Match m = parseLine.Match(lastProc);
if (m.Success)
{
Procedure myProc = this.Find(x => x.Number.Equals(m.Groups[1].ToString()) && x.Title.Equals(m.Groups[2].ToString()));
if (myProc == null) this.Add(myProc = new Procedure(m.Groups[1].ToString(), m.Groups[2].ToString()));
Page myPage = myProc.MyPages.Find(x => x.Number == pageNumber);
if (myPage == null) myProc.MyPages.Add(myPage = new Page(pageNumber));
myPage.MyLines.Add(new Line(text, myProc, myPage));
}
}
}
public partial class Page
{
private int _Number;
public int Number
{
get { return _Number; }
set { _Number = value; }
}
private Lines _MyLines = new Lines();
public Lines MyLines
{
get { return _MyLines; }
set { _MyLines = value; }
}
public Page(int number)
{
_Number = number;
}
public override string ToString()
{
return string.Format("Page {0}", Number);
}
}
public partial class Pages : List
{
public void Add(int number)
{
Add(new Page(number));
}
}
public partial class Line
{
private Procedure _MyProc;
public Procedure MyProc
{
get { return _MyProc; }
set { _MyProc = value; }
}
private Page _MyPage;
public Page MyPage
{
get { return _MyPage; }
set { _MyPage = value; }
}
private string _Text;
public string Text
{
get { return _Text; }
set { _Text = value; }
}
public Line(string text)
{
_Text = text;
}
public Line(string text, Procedure myProc, Page myPage)
{
_Text = text;
_MyProc = myProc;
_MyPage = myPage;
}
public override string ToString()
{
return Text;
}
}
public partial class Lines : List
{
public void Add(string text)
{
Add(new Line(text));
}
}
[Serializable]
public partial class IgnoreLine
{
private bool _Active = true;
public bool Active
{
get { return _Active; }
set { _Active = value; }
}
private Relation _SearchType = Relation.Contains;
public Relation SearchType
{
get { return _SearchType; }
set { _SearchType = value; }
}
private string _Text;
public string Text
{
get { return _Text; }
set { _Text = value; }
}
public IgnoreLine(string text, Relation searchType, bool active)
{
Text = text;
SearchType = searchType;
Active = active;
}
public IgnoreLine()
{
}
}
[Serializable]
public partial class IgnoreLines : BindingList
{
public IgnoreLines()
{
}
public void Add(string text, Relation searchType, bool active)
{
Add(new IgnoreLine(text, searchType, active));
}
// Convert IgnoreLines to string (XML)
public override string ToString()
{
return GenericSerializer.StringSerialize(this);
}
// Convert string to IgnoreLines
public static IgnoreLines Get(string xml)
{
return GenericSerializer.StringDeserialize(xml);
}
}
///
/// This is a simple serializer that takes a class and converts it to and from string (XML)
///
///
public static class GenericSerializer where T : class
{
public static string StringSerialize(T t)
{
string strOutput = string.Empty;
XmlSerializer xs = new XmlSerializer(typeof(T));
using (MemoryStream ms = new MemoryStream())
{
xs.Serialize(new NonXsiTextWriter(ms, Encoding.Unicode), t);
//xs.Serialize(ms, t);
ms.Position = 0;
StreamReader sr = new StreamReader(ms);
strOutput = sr.ReadToEnd();
ms.Close();
}
return strOutput;
}
public static T StringDeserialize(string s)
{
T t;
string ss = s.Replace("encoding=\"utf-16\"", "");
XmlSerializer xs = new XmlSerializer(typeof(T));
UTF8Encoding enc = new UTF8Encoding();
Byte[] arrBytData = enc.GetBytes(ss);
using (MemoryStream ms = new MemoryStream(arrBytData))
{
t = (T)xs.Deserialize(ms);
}
return t;
}
}
///
/// This XML Writer makes the XML more simple by excluding the XSI attributes from the serializer
///
public class NonXsiTextWriter : XmlTextWriter
{
public NonXsiTextWriter(TextWriter w) : base(w) { }
public NonXsiTextWriter(Stream w, Encoding encoding)
: base(w, encoding)
{
this.Formatting = Formatting.Indented;
}
public NonXsiTextWriter(string filename, Encoding encoding) : base(filename, encoding) { }
bool _skip = false;
public override void WriteStartAttribute(string prefix, string localName, string ns)
{
if ((prefix == "xmlns" && (localName == "xsd" || localName == "xsi")) || // Omits XSD and XSI declarations.
ns == XmlSchema.InstanceNamespace) // Omits all XSI attributes.
{
_skip = true;
return;
}
if (localName == "xlink_href")
base.WriteStartAttribute(prefix, "xlink:href", ns);
else
base.WriteStartAttribute(prefix, localName, ns);
}
public override void WriteString(string text)
{
if (_skip) return;
base.WriteString(text);
}
public override void WriteEndAttribute()
{
if (_skip)
{ // Reset the flag, so we keep writing.
_skip = false;
return;
}
base.WriteEndAttribute();
}
}
///
/// Class to support moving a process window
///
public class ProcessLocation
{
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
public const short SWP_NOMOVE = 0X2;
public const short SWP_NOSIZE = 1;
public const short SWP_NOZORDER = 0X4;
public const int SWP_SHOWWINDOW = 0x0040;
private System.Diagnostics.Process _Process;
public System.Diagnostics.Process Process
{
get { return _Process; }
set { _Process = value; }
}
private int _X;
public int X
{
get { return _X; }
set { _X = value; }
}
private int _Y;
public int Y
{
get { return _Y; }
set { _Y = value; }
}
public ProcessLocation(System.Diagnostics.Process process, int x, int y)
{
Process = process;
X = x;
Y = y;
}
private static Boolean FoxitSettingInfo = true;
///
/// MoveIt() moves the window containing the PDF viewer to the right so the two pdf viewer windows will not overlap.
///
public void MoveIt()
{
try
{
SetWindowPos(Process.MainWindowHandle, 0, X, Y, 200, 400, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOZORDER);
}
catch // B2018-147 bring up message to inform user of the setting that will create separate windows of the pdf viewer
{
if (FoxitSettingInfo)
MessageBox.Show("If you want to see the documents in separate windows,\nyou need to set Foxit to allow multiple instances. \nSelect File | preferences | Documents\n Check the Allow Multiple Instances", "Foxit Settings");
FoxitSettingInfo = false;
}
}
}
public class ProcessLocationQueue: Queue
{
public void Add(System.Diagnostics.Process process, int x, int y)
{
Enqueue(new ProcessLocation(process,x,y));
}
public void ProcessNext()
{
ProcessLocation pl = Dequeue();
pl.MoveIt();
}
}
}