Compare add

add support VS2012 dll
add compare objects tree
This commit is contained in:
levinsv 2019-12-22 22:53:56 +05:00
parent 7e10968209
commit 748d9b7950
31 changed files with 3971 additions and 4 deletions

View file

@ -80,6 +80,14 @@ This text Russian language.
- добавлена поддержка отображения дополнительных опция для индексов
- в окне запросов добавлена альтернативная кнопка отражающая текущий режим, Transaction (T) или AutoCommit (A)
* исправлена ошибка в окне поиска объектов при поиске в коментариях
22.12.2019
- добавлена возможность выполнять сравнение описания объектов разных серверов через меню Отчеты "Compare other objects"
Сравнение проводится с другим открытым соединением и подключенной базой. Объекты для сравнения выбираются по дереву вниз.
По результатам формируется html отчет различий.
В качестве шаблона для отчета используется файл textcompare_report.template, находящийся рядом с исполняемым pgadmin3.exe.
Особенности: SQL текст создания последовательностей игнорируется, секции таблиц не учитываются. Полность одинаковые объекты скрываются. Служебные объекты игнорируются.
- выполнен переход на новые библиотеки dll wxWidgets 3.0.4 скомпилированные под VS2012. Необходимо обновить файлы *.dll

Binary file not shown.

View file

@ -0,0 +1,276 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="description" content="Text Compare! is an online diff tool that can find the difference between two text documents. Just paste and compare.">
<title>Compare object DateBase </title>
<body style="zoom: 1;">
&nbsp;
<style type="text/css">
body {
/*font-family: monospace;*/
width : 100%;
text-align:center;
background: white;
padding: 0;
margin: 0;
}
div.logo {
background-repeat: repeat;
/*background-position: top;*/
height: 30px;
/*overflow: hidden;*/
background-color: #5CA96D;
background-image: linear-gradient(90deg, #4E905D, #6BC57F);
}
div.logocompare {
display: block;
margin-left: auto;
margin-right: auto;
color: #f9f9f9;
font-size: 20px;
font-weight: bold;
text-decoration: none;
text-shadow:1px 1px grey;
padding-top: 10px;
}
.logost {
display: block;
margin-left: auto;
margin-right: auto;
color: #f9f9f9;
font-size: 16px;
font-weight: bold;
text-decoration: none;
text-shadow:1px 1px grey;
padding-top: 5px;
}
div.logoheader {
background-repeat: repeat;
/*background-position: top;*/
height: 40px;
/*overflow: hidden;*/
background-color: #4488C7;
background-image: linear-gradient(90deg, #0B70CD, #5C90C0);
}
.logo_background {
/* background-image:url('tiny_grid-transparent.png'); */
width: 100%;
height: 100%;
}
div.logo, div.mainContent, .addThisFooter, div.logocompare {
text-align:center;
/*width: 95%;*/
margin-left: auto;
margin-right: auto;
}
.addThisFooter {
margin-bottom: 150px;
}
.logotext {
display: block;
margin-left: auto;
margin-right: auto;
color: #f9f9f9;
font-size: 25px;
font-weight: bold;
text-decoration: none;
text-shadow:1px 1px grey;
padding-top: 5px;
}
table {
font-size: 12px;
}
div.mainContent {
background: #eee;
/*background-image:url('tc-bgtop-bottom.png'); */
background-repeat: repeat-x;
background-position: top;
/*border-bottom: 2px white solid;*/
margin-bottom: 15px;
border-bottom: 1px solid #ddd;
border-top: 1px solid #ddd;
}
.lineContent {
width: 49%;
}
.lineContent pre {
/* Undoing bootstrap CSS for PRE tags */
padding: 0;
margin: 0;
background-color: white;
border: 0;
border-radius: 0;
line-height: normal;
padding-left: 2px;
padding-right: 2px;
/* Make sure really long strings are wrapped. */
background: white;
margin: 0;
-ms-word-break: break-all;
-ms-word-wrap: break-all;
-webkit-word-break: break-word;
-webkit-word-wrap: break-word;
word-break: break-word;
word-wrap: break-word;
-webkit-hyphens: auto;
-moz-hyphens: auto;
hyphens: auto;
white-space: pre-wrap;
}
.text-compare {
font-size: 12px;
}
img {
border: none;
}
.navImage {
border:0px;
padding-left:5px;
padding-right:5px;
margin:0px;
}
.delimiter {
heigth:40px;
display: inline-block;
}
label {
font-size: 14px;
font-weight: normal !important;
margin-bottom: 0px !important;
}
h2 { font-size: 130%; padding-bottom: 0.5ex; color: #009ACE; border-bottom-style: solid; border-bottom-width: 2px; }
h3 { font-size: 110%; padding-bottom: 0.5ex; color: #000000; }
.text-compare {
border: 1px solid #ababab;
background: white;
line-height: normal;
}
.text-compare, div.sendEmail {
width: 98%;
margin-left: auto;
margin-right: auto;
}
tbody {
border: none;
}
.text-compare td {font-family: monospace;}
colgroup {border-color: #ababab;}
.link_first {
background-image:url('tc-arrow-down-green.png');
background-position: top;
background-repeat: no-repeat;
}
.link_next {
background-image:url('tc-arrow-down-blue.png');
background-position: top;
background-repeat: no-repeat;
}
.link_top {
background-image:url('tc-arrow-up-orange.png');
background-position: top;
background-repeat: no-repeat;
}
.link_top, .link_next, .link_first {
height: 12px;
width: 11px;
}
.link_section {
height: 18px;
}
.text-section {
font-size: 15px;
}
.diff_next {
width: 11px;
}
.text_next, .text_first, .text_top {visibility: hidden;}
td {
padding-left: 2px;
padding-right: 2px;
}
.diff_header { width: 1em;}
.difference {
width: 1em;
background: #A9D0F5;
}
.has_difference {
background: #97D397;
width: 1em;
}
.diff_header, .diff_next {
vertical-align:top;
background: #dedede;
}
.diff_next {padding-top:2px;}
.diff_remove {background: Pink;}
.diff_insert {background: SkyBlue;}
.diff_eq {background: #eeeeee;}
.diff_eqhidden {display: none;}
.diff_ne {background: LightGreen;}
.lineContent {
vertical-align:top;
text-align:left;
}
</style>
<div class="mainContent">
$titleline$
@titleline <h2 id="group-list" style="text-align: left;">$titleline$</h2>
$list$
@[list <ul type="square" style="text-align: left;">
@rowlist <li id="_@idtablecmp@" class="diff_@color@"><a href="#@idtablecmp@"> $rowlist$ </a></li>
@]list </ul>
@tableheader@
<div class="logoheader">
<div class="logo_background">
<a id="@idtablecmp@" href="#_@idtablecmp@" class="logotext">$rowlist$</a>
</div>
</div>
@tableheader@
@tableheader2 <table class="text-compare" id="t@idtablecmp@" cellspacing="0" cellpadding="0" rules="groups"> <colgroup></colgroup> <colgroup></colgroup> <colgroup></colgroup> <colgroup></colgroup> <colgroup></colgroup> <colgroup></colgroup> <tbody>
$tables$
</div>
</body></html>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -739,7 +739,7 @@ void ctlSQLBox::OnKeyDown(wxKeyEvent &event)
int max = line.Length() - (GetLineEndPosition(GetCurrentLine()) - GetCurrentPos()) - offset;
if(line != wxEmptyString)
{
while ((line[x].GetValue() == '\t' || line[x].GetValue() == ' ') && x < max) {
while ((x < max) &&(line[x].GetValue() == '\t' || line[x].GetValue() == ' ')) {
wxChar ccc=line[x];
indent += line[x++];
}

View file

@ -431,7 +431,8 @@ void frmMain::CreateMenus()
new reportObjectDependenciesFactory(menuFactories, reportMenu, 0);
new reportObjectDependentsFactory(menuFactories, reportMenu, 0);
new reportObjectListFactory(menuFactories, reportMenu, 0);
new reportCompareFactory(menuFactories, reportMenu, 0);
toolsMenu->AppendSeparator();

View file

@ -32,6 +32,8 @@
#include "schema/pgForeignKey.h"
#include "schema/pgIndexConstraint.h"
#include "schema/pgCheck.h"
#include "utils/utffile.h"
#include <wx/stdpaths.h>
// XML2/XSLT headers
#include <libxslt/transform.h>
@ -1157,6 +1159,698 @@ cleanup:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// END XML FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
SQL::SQL(const wxString &_sql, const wxString &_pathtree) {
// Construct a SQL with the specified operation and text.
sql=wxString(_sql);
pathtree=wxString(_pathtree) ;
countchild=0;
mode=0;
}
SQL::SQL() {
}
/**
* Display a human-readable version of this SQL.
* @return text version
*/
wxString SQL::toString() const {
wxString prettyText = sql;
// Replace linebreaks with Pilcrow signs.
//prettyText.replace('\n', L'\u00b6');
wxString c=wxEmptyString;
wxString cmp=wxEmptyString;
if (countchild>0) c.Printf(wxT("(%d)"), countchild);
bool r=false;
if (sql.Cmp(sql2)!=0) {
cmp="ne EQ ";
cmp.Printf(wxT("(ne EQ (%d, %d))"), sql.Len(),sql2.Len());
}
if (mode==__Remove) cmp=cmp+" - ";
if (mode==__Insert) cmp=cmp+" + ";
if (mode==__Equal) cmp=cmp+" = ";
return wxString(pathtree+c+"\n"+cmp+"SQL(" + wxString("\""))
+ prettyText + wxString("\")");
}
/**
* Is this SQL equivalent to another SQL?
* @param d Another SQL to compare against
* @return true or false
*/
bool SQL::operator==(const SQL &d) const {
return (d.sql == this->sql);
}
bool SQL::operator!=(const SQL &d) const {
return !(operator == (d));
}
#include <wx/listimpl.cpp>
WX_DEFINE_LIST(MyListSql);
#include "wx/arrimpl.cpp"
WX_DEFINE_OBJARRAY(ArraySQL)
wxString reportCompareFactory::GetNodePath(wxTreeItemId node) {
wxString path;
path = parent->GetBrowser()->GetItemText(node).Trim();
wxTreeItemId parent_id = parent->GetBrowser()->GetItemParent(node);
while (parent_id.IsOk())
{
path = parent->GetBrowser()->GetItemText(parent_id).BeforeFirst('(').Trim() + wxT("/") + path;
parent_id = parent->GetBrowser()->GetItemParent(parent_id);
}
return path;
}
void reportCompareFactory::GetExpandedChildNodes(wxTreeItemId node, wxArrayString &expandedNodes, ArraySQL &list, time_t *t, wxBusyInfo *w, MyHashSQL &h_path,int lvl)
{
wxTreeItemIdValue cookie;
ctlTree *browser=parent->GetBrowser();
wxTreeItemId child;
if (lvl==0) child = node;
else child = browser->GetFirstChild(node, cookie);
pgObject *obj;
wxString path;
time_t tmp;
int size=expandedNodes.Count();
while (child.IsOk())
{
obj=browser->GetObject(child);
if (obj && obj->GetMetaType()==PGM_FUNCTION && !browser->HasChildren(child))
path=GetNodePath(child);
else
path=parent->GetNodePath(child);
// tmp=wxDateTime::GetTimeNow();
// if (difftime(tmp,*t)>2.0) {
//w->~wxBusyInfo();
//wxSafeYield();
//wxMilliSleep(50);
//wxSafeYield();
//w = new wxBusyInfo(wxString::Format("Path = %s ,GetName() = %s, isCollection = %d",
// path.c_str(), obj->GetName().c_str(), obj->IsCollection()),parent);
//wxSafeYield();
//wxMilliSleep(50);
//wxSafeYield();
// *t=tmp;
// }
if (obj) {
// OutputDebugString(wxString::Format("Path = %s ,GetName() = %s, isCollection = %d\n",
// path.c_str(), obj->GetTypeName().c_str(), obj->IsCollection()));
if (obj->GetMetaType()==PGM_CATALOG
||obj->GetMetaType()==PGM_COLUMN
||obj->GetMetaType()==PGM_RULE
||obj->GetMetaType()==PGM_CATALOG
) {
child = browser->GetNextChild(node, cookie);
continue;
}
//obj->ShowTreeDetail(browser);
//obj->ShowTree(parent,browser);
// åñëè íàäî îáúåêòî ñëîæíûé è ñàñ ñîñòîèò èç êîëëåêöèé
if ((obj->GetMetaType()==PGM_SCHEMA
||obj->GetMetaType()==PGM_DATABASE
||obj->GetMetaType()==PGM_TABLE
||obj->GetMetaType()==PGM_FOREIGNTABLE
)&&!obj->IsCollection()) {
obj->ShowTreeDetail(browser);
//obj->ShowTree(parent,browser);
} else
{
if (obj->GetMetaType()==PGM_VIEW) obj->ShowTreeDetail(browser); // òîëüêî äëÿ òîãî ÷òîáû ïîëó÷èòü èíôó î òðèããåðàõ
if (obj->GetMetaType()==PGM_EVENTTRIGGER) // ïîëó÷àåì èíôó î òðèãåððàõ ïî ñîáûòèÿì
obj->ShowTreeDetail(browser);
}
}
//if (browser->IsExpanded(child))
if (browser->HasChildren(child))
{
bool rec=true;
if (obj && (obj->GetMetaType()==PGM_TABLE
//||obj->GetMetaType()==PGM_VIEW
)) {
wxTreeItemId Item = browser->GetItemParent(child);
obj=browser->GetObject(Item); // Tables
wxTreeItemId Item2 = browser->GetItemParent(obj->GetId());
obj=browser->GetObject(Item2); // Schemes
if (obj && obj->GetMetaType()==PGM_SCHEMA&& !obj->IsCollection()) {
rec=false; // íå ñîáèðàåì èíôó ïî ñåê. òàáëèöàì è ñåêöèÿì, è âî âíóòðü íå çàõîäèì
obj=browser->GetObject(child);
obj->ShowTreeDetail(browser);
} else obj=browser->GetObject(child);
}
if (obj && (obj->GetMetaType()==PGM_VIEW && !obj->IsCollection())) rec=false;
if (rec) {
GetExpandedChildNodes(child, expandedNodes,list,t,w,h_path,lvl+1);
//expandedNodes.Add(parent->GetNodePath(child));
obj=browser->GetObject(child);
}
}
if (obj ) {
wxString s=obj->GetSql(browser);
if (obj->GetMetaType()==PGM_SEQUENCE) s="";
int c=browser->GetChildrenCount(child,false);
if (size>0) {
wxString srcpath(path);
SQL *sq;
srcpath.Replace(expandedNodes[0],expandedNodes[2],false);
srcpath.Replace(expandedNodes[1],expandedNodes[3],false);
MyHashSQL::iterator it=h_path.find(srcpath);
if (h_path.end()==it) {
// íå íàéäåíî â ïåðâîé ÁÄ
if (s!=wxEmptyString) {
sq =new SQL(wxEmptyString,srcpath);
sq->sql2=s;
sq->mode=__Remove;
list.Add(sq);
} else
{
sq =new SQL(wxEmptyString,srcpath);
sq->sql2=wxEmptyString;
sq->countchild=c;
sq->mode=__Remove;
list.Add(sq);
}
h_path[srcpath]=list.GetCount()-1;
}
else {
int i=it->second;
SQL& sql=list.Item(i);
sql.mode=__Equal;
sql.sql2=s;
}
} else {
if (s!=wxEmptyString) {
SQL *sq =new SQL(s,path);
sq->mode=__Insert;
list.Add(sq);
} else
{
SQL *sq =new SQL(s,path);
sq->countchild=c;
sq->mode=__Insert;
list.Add(sq);
}
h_path[path]=list.GetCount()-1;
}
}
child = browser->GetNextChild(node, cookie);
}
}
wxWindow *reportCompareFactory::StartDialog(frmMain *form, pgObject *obj)
{
parent = form;
// std::wstring r;
// std::wstring str1=L"";
// std::wstring str2=L"line#\nline2\nADD line 3\nline4\n";
// r=printdiff(str1,str2);
// return 0;
// wxBeginBusyCursor();
//frmReport *report = new frmReport(GetFrmMain());
//wxBusyInfo *waiting;
wxString msg;
// Generate the report header
wxDateTime now = wxDateTime::Now();
ctlTree *browser=form->GetBrowser();
wxTreeItemIdValue foldercookie;
wxTreeItemId folderitem = browser->GetFirstChild(browser->GetRootItem(), foldercookie);
wxString path(form->GetNodePath(obj->GetId()));
// ãðóïïû ñåðâåðîâ/Ñåðâåðû/serverN/Datebases/dbname
// p1 p2 p3
wxString p_db;
int p1=path.Find('/');
if (p1<0) return 0;
int p2=path.find('/',p1+1);
if (p2<0) return 0;
startpathpos=p2;
int p3=path.find('/',p2+1);
wxString p_pref=path.substr(0,p2);
wxString p_server_obj;
if (!obj->GetConnection()) return 0;
if (obj && obj->GetDatabase() && obj->GetDatabase()->GetConnected())
p_db=obj->GetDatabase()->GetName();
else
p_db=obj->GetServer()->GetDatabaseName();
if (p3<0) {
// âûáðàí ñåðâåð
//if (wxMessageBox(wxString::Format("Path = %s ,GetName() = %s, isCollection = %d",
// path.c_str(), obj->GetTypeName().c_str(), obj->IsCollection()), _("Close"), wxYES_NO | wxICON_QUESTION) != wxYES)
//{
// return 0;
//}
//Ãðóïïû ñåðâåðîâ/Ñåðâåðû/PostgreSQL 9.6
// èñïîëüçóåì ïåðâóþ ïîïàâøóþñÿ îòêðûòóþ ÁÄ
p_server_obj=path.substr(p2); // ñ /ñåðâåðN/
} else
{
// Ãðóïïû ñåðâåðîâ/Ñåðâåðû/PostgreSQL 9.6/Áàçû äàííûõ/postgres
p_server_obj=path.substr(p2,p3-p2); // ñ /ñåðâåðN/
}
wxString p_db_replace=_("Databases")+"/"+p_db+"/";
wxString p_server_replace=_("Servers")+p_server_obj;
pgServer *server;
pgDatabase *db=NULL,*lastdb=NULL;
wxString trg_db_replace;
wxString trg_server_replace;
wxTreeItemId srvitem = obj->GetServer()->GetId();
while (folderitem)
{
if (browser->ItemHasChildren(folderitem))
{
wxTreeItemIdValue servercookie;
wxTreeItemId serveritem = browser->GetFirstChild(folderitem, servercookie);
while (serveritem)
{
server = (pgServer *)browser->GetItemData(serveritem);
if (server != NULL && server->IsCreatedBy(serverFactory))
{
trg_server_replace=browser->GetItemText(server->GetId()).BeforeFirst('(').Trim();
if (srvitem!=server->GetId() && server->GetConnected()) {
// íàøå ñîåäèíåíèå íå íóæíî íóæíî äðóãîå è àêòèâíîå
pgCollection *coll = browser->FindCollection(databaseFactory, server->GetId());
if (coll)
{
treeObjectIterator dbs(browser, coll);
while ((db = (pgDatabase *)dbs.GetNextObject()) != 0)
{
// åñòü îòêðûòàÿ ÁÄ
lastdb=db;
if (db->GetConnected()) {
//if (db->GetName()!=wxT("postgres")) goto ex_out;
goto ex_out;
}
}
}
}
}
serveritem = browser->GetNextChild(folderitem, servercookie);
}
}
folderitem = browser->GetNextChild(browser->GetRootItem(), foldercookie);
}
ex_out:
pgObject *trgobj;
wxString newpath;
if (lastdb!=NULL) {
p_db=browser->GetItemText(lastdb->GetId()).BeforeFirst('(').Trim();
trg_db_replace=_("Databases")+"/"+p_db+"/";
trg_server_replace=_("Servers")+"/"+trg_server_replace;
newpath=path;
//if (newpath.Replace(p_server_replace,trg_server_replace,false)==0)
newpath.Replace(p_server_replace,trg_server_replace,false);
newpath.Replace(p_db_replace,trg_db_replace,false);
if (!parent->SetCurrentNode(parent->GetBrowser()->GetRootItem(),newpath)) {
msg.Printf("Íå óäàëîñü íàéòè îáúåêò %s â äðóãîé ÁÄ.",newpath);
wxMessageBox(msg, _("Error"), wxOK | wxICON_INFORMATION);
return 0;
}
trgobj=browser->GetObject(browser->GetSelection());
} else
{
msg="Íåò äðóãèõ óñòàíîâëåííûõ ñîåäèíåíèè , ñðàâíåíèå íå âîçìîæíî.";
// msg.Printf("Â óñòàíîâëåííîì ñîåäèíåíèè %s, íåò ïîäõîäÿùèõ ÁÄ.",browser->GetItemText(lastdb->GetServer()->GetId()).BeforeFirst('(').Trim());
wxMessageBox(msg, _("Error"), wxOK | wxICON_INFORMATION);
return 0;
}
time_t timer=wxDateTime::GetTimeNow();
wxArrayString expandedNodes;
ArraySQL list;
MyHashSQL h_path;
wxWindowDisabler disableAll;
{
wxBusyInfo waiting(wxString::Format(" Îáõîä èñõîäíîé ÁÄ Path = %s ,Ñòàðòîâûé îáúåêò = %s",
browser->GetItemText(obj->GetServer()->GetId()).c_str(), obj->GetName().c_str(),parent));
// Give the UI a chance to redraw
wxSafeYield();
wxMilliSleep(50);
wxSafeYield();
// waiting->~wxBusyInfo();
GetExpandedChildNodes(obj->GetId(),expandedNodes,list,&timer, NULL,h_path,0);
}
//waiting->~wxBusyInfo();
wxFileName fn("");
fn.MakeAbsolute();
wxSafeYield();
wxMilliSleep(50);
wxSafeYield();
// return 0;
{
wxBusyInfo waiting(wxString::Format(" Îáõîä äðóãîé ÁÄ Path = %s\nÑòàðòîâûé îáúåêò = %s",
browser->GetItemText(trgobj->GetId()).c_str(), trgobj->GetName().c_str(),parent));
// Give the UI a chance to redraw
wxSafeYield();
wxMilliSleep(50);
wxSafeYield();
timer=wxDateTime::GetTimeNow();
wxArrayString expandedNodes2;
expandedNodes2.Add(trg_server_replace);
expandedNodes2.Add(trg_db_replace);
expandedNodes2.Add(p_server_replace);
expandedNodes2.Add(p_db_replace);
GetExpandedChildNodes(trgobj->GetId(),expandedNodes2,list,&timer, NULL,h_path,0);
}
//waiting->~wxBusyInfo();
int e_count=list.GetCount();
int linecount=0;
int nstart=0,lastpos=0;
int c,minlen=100000,root=0;
wxArrayInt* child;
wxArrayPtrVoid level(15);
wxHashTable htab(wxKEY_STRING);
wxString key;
int npos=p2;
for (int i = 0; i < e_count; ++i)
{
SQL& sq = list.Item(i);
c=0;
nstart=0;
if ((nstart=sq.pathtree.rfind('/'))!=wxNOT_FOUND ) {
key=sq.pathtree.SubString(npos,nstart-1);
if (minlen>key.Len()) {minlen=key.Len(); root=i;}
}
child=(wxArrayInt *)htab.Get(key);
if (child==NULL) {
child=new wxArrayInt;
htab.Put(key,(wxObject *)child);
}
child->Add(i);
linecount++;
}
MyListSql::iterator iter2;
//e_count=list.GetCount();
//for (int i = 0; i < e_count; ++i)
//{
// SQL& sq = list.Item(i);
//}
// file2.Write(report, wxConvUTF8);
// file2.Close();
//return 0;
titleline=wxEmptyString;
list_head=wxEmptyString;
rowlist=wxEmptyString;
list_end=wxEmptyString;
tableheader=wxEmptyString;
head=wxEmptyString;
tableheader2=wxEmptyString;
tableshtml=wxEmptyString;
// Çàãðóçêà øàáëîíà
#ifndef _DEBUG
wxString fDir=wxStandardPaths::Get().GetExecutablePath().BeforeLast('\\')+wxT("\\");
#else
wxString fDir=wxStandardPaths::Get().GetExecutablePath().BeforeLast('\\')+wxT("\\");
#endif
wxString f=fDir+"textcompare_report.template";
wxString buffer;
wxUtfFile file3(f, wxFile::read, wxFONTENCODING_UTF8);
if (file3.IsOpened())
{
file3.Read(buffer);
file3.Close();
} else
{
wxLogError(_("Failed to open file %s."), f.c_str());
return 0;
}
wxStringTokenizer lines(buffer, wxT("\n"));
bool mline=false;
while (lines.HasMoreTokens())
{
wxString tmp = lines.GetNextToken();
wxString line = tmp.Strip(wxString::both);
if (tmp.StartsWith("@end@")) break;
int l=wxString("@titleline").Len();
if (tmp.StartsWith("@titleline")) {titleline=tmp.Mid(l)+"\n"; continue;}
l=wxString("@[list").Len();
if (tmp.StartsWith("@[list")) {list_head=tmp.Mid(l)+"\n"; continue;}
l=wxString("@]list").Len();
if (tmp.StartsWith("@]list")) {list_end=tmp.Mid(l)+"\n"; continue;}
l=wxString("@rowlist").Len();
if (tmp.StartsWith("@rowlist")) {rowlist=tmp.Mid(l)+"\n"; continue;}
l=wxString("@tableheader2").Len();
if (tmp.StartsWith("@tableheader2")) {tableheader2=tmp.Mid(l)+"\n"; continue;}
if (tmp==wxString("@tableheader@")) {mline=!mline; continue;}
if (mline) { tableheader+=tmp+"\n";continue;}
head+=tmp+"\n";
}
//
{
wxBusyInfo waiting(wxString::Format(" Ïîèñê ðàçëè÷èé ...",0));
// Give the UI a chance to redraw
wxSafeYield();
wxMilliSleep(50);
wxSafeYield();
wxString content=printlvl(root,0,list,htab);
wxString tmp(titleline);
tmp.Replace("$titleline$","Left : "+path);
wxString tmp2(titleline);
tmp2.Replace("$titleline$","Right: "+newpath);
head.Replace("$titleline$",tmp+tmp2);
head.Replace("$list$",content);
head.Replace("$tables$",tableshtml);
tableshtml=wxEmptyString;
content=wxEmptyString;
}
//head+="</div></body></html>";
fDir=wxStandardPaths::Get().GetTempDir()+wxT("\\cmp.html");
//fn="D:\\PostgreSQL\\cmp.html";
fn=fDir;
fn.MakeAbsolute();
wxString html(head);
wxFile file4(fn.GetFullPath(), wxFile::write);
if (!file4.IsOpened())
{
wxLogError(_("Failed to open file %s."), fn.GetFullPath().c_str());
return 0;
}
file4.Write(html, wxConvUTF8);
file4.Close();
head=wxEmptyString;
list.RemoveAt(0,list.GetCount());
e_count=list.GetCount();
for (int i = 0; i < e_count; ++i)
{
//SQL *p=&sq;
//list.RemoveAt(0,list.GetCount());
// SQL *p = list.Detach(i);
// delete p;
}
//htab.DeleteContents(true);
h_path.clear();
//MyHashSQL::iterator it, en;
//for( it = h_path.begin(), en = h_path.end(); it != en; ++it )
//{
// h_path.erase(it);
//}
//htab.Clear();
//list.Empty();
#ifdef __WXMSW__
wxLaunchDefaultBrowser(fn.GetFullPath());
#else
wxLaunchDefaultBrowser(wxT("file://") + fn.GetFullPath());
#endif
return 0;
}
#if defined(DELETE)
#undef DELETE
#endif // DUMMYSTRUCTNAME
std::wstring reportCompareFactory::printdiff(std::wstring str1, std::wstring str2 )
{
diff_match_patch dmp;
std::list<Diff> diffs;
if (str1==str2) {
return L"";
}
diffs=dmp.diff_main(str1,str2);
int nstart=0;
int pos=0;
countdiffline=0;
std::wstring cur_l;
std::wstring ncur_l; std::wstring p_ncur_l;std::wstring p_ncur_r;
std::wstring cur_r;
std::wstring ncur_r;
std::wstring tex;
std::wstring t;
std::wstring tableline;
int rline=1,lline=1;
std::list<Diff>::const_iterator it; // îáúÿâëÿåì èòåðàòîð
it = diffs.begin(); // ïðèñâàèâàåì åìó íà÷àëî ñïèñêà
Diff aDiff;
bool modify=false;
bool oneline=false;
nstart=0;
while (it != diffs.end()) // ïîêà èòåðàòîð íå äîñòèãíåò êîíöà
{
aDiff=*it;
tex=aDiff.text;
nstart=0;
while (nstart<tex.length()) {
pos=tex.find('\n',nstart);
if (pos==-1) {t.assign(tex,nstart,tex.length());nstart=tex.length();} else {t.assign(tex,nstart,pos-nstart);nstart=pos;}
if (t.length()>0) {
// ýòî âñ¸ åù¸ îäíà ñòðîêà
if (aDiff.operation==Operation::INSERT) cur_r+=L"<span class=\"difference\">"+t+L"</span>";
if (aDiff.operation==Operation::DELETE) cur_l+=L"<span class=\"difference\">"+t+L"</span>";
if (aDiff.operation==Operation::EQUAL) {
cur_r+=t;
cur_l+=t;
} else modify=true;
// ïîêà íå âñòðåòèì ïåðåâîä ñòðîêè ñ÷èòàåì ÷òî ýòî âñ¸ îäíà ñòðîêà
oneline=true;
} else
{
// äîøëè äî ïåðåâîäà \n
nstart=pos+1;
ncur_l=std::to_wstring(lline);
ncur_r=std::to_wstring(rline);
if (p_ncur_r==ncur_r) { ncur_r=L""; modify=true;}
if (p_ncur_l==ncur_l) { ncur_l=L""; modify=true;}
if (modify) countdiffline++;
// ôîðìèðóåì êîëîíêè
//left
tableline+=L"<tr><td class=\"diff_next\"></td>";
tableline+= modify ? L"<td class=\"has_difference\">"+ncur_l+"</td>" : L"<td class=\"diff_header\">"+ncur_l+"</td>";
tableline+=L"<td class=\"lineContent\"><pre>"+cur_l+"</pre></td>";
// right
tableline+=L"<td class=\"diff_next\"></td>";
tableline+= modify ? L"<td class=\"has_difference\">"+ncur_r+"</td>" : L"<td class=\"diff_header\">"+ncur_r+"</td>";
tableline+=L"<td class=\"lineContent\"><pre>"+cur_r+"</pre></td>";
tableline+=L"</tr>";
if (aDiff.operation==Operation::INSERT) {
rline++;
} else if (aDiff.operation==Operation::DELETE) {
lline++;
} else if (aDiff.operation==Operation::EQUAL) {
rline++; lline++;
}
if (ncur_r.length()>0) p_ncur_r=ncur_r;
if (ncur_l.length()>0) p_ncur_l=ncur_l;
cur_r=L"";cur_l=L""; ncur_l=L""; ncur_r=L"";
modify=false;
oneline=false;
}
} // öèêë ïî ñòðîêàì âíóòðè îäíîãî Diff
++it;
}
#ifdef _DEBUG
wxFileName fn("D:\\PostgreSQL\\cmp.txt");
fn.MakeAbsolute();
wxFile file(fn.GetFullPath(), wxFile::write);
if (!file.IsOpened())
{
wxLogError(_("Failed to open file %s."), fn.GetFullPath().c_str());
}
wxString report;
report.Append(wxString::Format(" Òàáëèöà\n%s\n",tableline));
file.Write(report, wxConvUTF8);
file.Close();
#endif
return tableline;
}
wxString reportCompareFactory::printlvl(int element,int lvl,ArraySQL &list, wxHashTable &htab)
{
wxString l(list_head);
wxString r=wxEmptyString;
wxArrayInt* child;
wxString key,name;
int e=element;
int nstart;
SQL& sq=list.Item(e);
if ((nstart=sq.pathtree.rfind('/'))!=wxNOT_FOUND ) {
name=sq.pathtree.AfterLast('/');
key=sq.pathtree.Mid(startpathpos);
}
wxString tid=wxString::Format("id%d",e);
wxString rlist=HtmlEntities(name);
wxString cdiff=wxEmptyString;
// òàáëèöà ðàçëè÷èé
countdiffline=0;
if (sq.sql.length()+sq.sql2.length()>0) {
// äëÿ îäèíàêîâûõ íåáóäêì òàáëèöó ôîðìèðîâàòü
std::wstring t1=sq.sql.wc_str();
std::wstring t2(sq.sql2.wc_str());
std::wstring rez=printdiff(t1,t2);
if (rez.length()>0) {
//L"</tbody></table>"
//tableshtml
wxString tmp(tableheader);
tmp.Replace("@idtablecmp@",tid);
tmp.Replace("$rowlist$",rlist);
tableshtml+=tmp+"\n";
tmp=tableheader2;
tmp.Replace("@idtablecmp@",tid);
tmp.Replace("$rowlist$",rlist);
tableshtml+=tmp;
tableshtml+=rez;
tableshtml+="</tbody></table>\n";
cdiff=wxString::Format(" ( %d )",countdiffline);
}
}
// ñïèñîê îáúåêòîâ
child=(wxArrayInt *)htab.Get(key);
r=rowlist;
r.Replace("$rowlist$",rlist+cdiff);
if (sq.mode==__Insert) r.Replace("@color@","insert");
else if (sq.mode==__Remove) r.Replace("@color@","remove");
else if (countdiffline>0) r.Replace("@color@","ne"); else {
if (child!=NULL) r.Replace("@color@","eq"); else r.Replace("@color@","eqhidden");
}
r.Replace("@idtablecmp@",tid);
l+=r;
if (child!=NULL) {
for (int j=0;j<child->GetCount();j++) {
l+=printlvl(child->Item(j),lvl+1,list,htab);
}
}
l+=list_end;
return l;
}
///////////////////////////////////////////////////////
// Report base

View file

@ -15,6 +15,8 @@
#include "dlg/dlgClasses.h"
#include "ctl/ctlListView.h"
#include "ctl/ctlSQLResult.h"
#include "schema/pgDatabase.h"
#include <wx/busyinfo.h>
// Class declarations
class frmReport : public pgDialog
@ -86,7 +88,74 @@ public:
return false;
};
};
class SQL;
WX_DECLARE_LIST(SQL, MyListSql);
WX_DECLARE_OBJARRAY(SQL, ArraySQL);
class SQL {
public:
// One of: INSERT, DELETE or EQUAL.
wxString sql;
wxString sql2;
wxString pathtree;
int countchild,mode;
// The text associated with this diff operation.
/**
* Constructor. Initializes the diff with the provided values.
* @param operation One of INSERT, DELETE or EQUAL.
* @param text The text being applied.
*/
SQL(const wxString &_sql, const wxString &_pathtree);
SQL();
// inline bool isNull() const;
wxString toString() const;
bool operator==(const SQL &d) const;
bool operator!=(const SQL &d) const;
//static wxString strOperation(Operation op);
};
WX_DECLARE_STRING_HASH_MAP( int, MyHashSQL );
#define __Remove 1
#define __Equal 0
#define __Insert 2
class reportCompareFactory : public actionFactory
{
private:
wxString titleline;
wxString list_head;
wxString rowlist;
wxString list_end;
wxString tableheader;
wxString head;
wxString tableheader2;
wxString tableshtml;
int startpathpos,countdiffline;
protected:
//reportCompareFactory(menuFactoryList *list) : actionFactory(list) {}
wxString reportCompareFactory::GetNodePath(wxTreeItemId node);
wxWindow *StartDialog(frmMain *form, pgObject *obj);
void reportCompareFactory::GetExpandedChildNodes(wxTreeItemId node, wxArrayString &expandedNodes, ArraySQL &list,time_t *t,wxBusyInfo *w, MyHashSQL &h_path,int lvl);
std::wstring reportCompareFactory::printdiff(std::wstring str1, std::wstring str2 );
wxString printlvl(int element,int lvl,ArraySQL &list, wxHashTable &htab);
frmMain *GetFrmMain()
{
return parent;
};
frmMain *parent;
public:
reportCompareFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar): actionFactory(list) {
mnu->Append(id, _("&Compare other objects"), _("Compare other host database select objects."));
};
bool CheckEnable(pgObject *obj)
{
return true;
};
};
///////////////////////////////////////////////////////
// Object properties report

View file

@ -11,6 +11,8 @@
#ifndef PGADMIN3_H
#define PGADMIN3_H
#include "../utils/diff_match_patch.h"
// wxWindows headers
#include <wx/wx.h>

View file

@ -23,6 +23,7 @@ class pgSchema;
class pgForeignDataWrapper;
class pgForeignServer;
class pgUserMapping;
class pgproJob;
// Class declarations
class pgCollection : public pgObject
@ -65,6 +66,10 @@ public:
{
return job;
}
pgproJob *GetproJob() const
{
return projob;
}
int GetIconId();
pgaFactory *GetItemFactory()
@ -99,6 +104,7 @@ protected:
pgForeignDataWrapper *fdw;
pgForeignServer *fsrv;
pgUserMapping *um;
pgproJob *projob;
};

View file

@ -32,6 +32,7 @@ pgCollection::pgCollection(pgaFactory *factory)
schema = 0;
database = 0;
server = 0;
projob = 0;
}
bool pgCollection::IsCollectionFor(pgObject *obj)

View file

@ -125,7 +125,7 @@ void pgEventTrigger::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView
delete eventTriggerFunction;
wxString restr = wxT(" WHERE nsp.oid= ") + NumToStr(GetSchemaOid()) + wxT("::oid\n");
eventTriggerFunctionSchema = (pgSchema *)schemaFactory.CreateObjects((pgCollection *)browser->GetObject(browser->GetSelection()), 0, restr);
eventTriggerFunctionSchema = (pgSchema *)schemaFactory.CreateObjects((pgCollection *)browser->GetObject(GetId()), 0, restr);
// append function here
eventTriggerFunction = functionFactory.AppendFunctions(this, eventTriggerFunctionSchema, browser, wxT(" WHERE pr.oid = ") + NumToStr(functionOid) + wxT("::oid\n"));
@ -162,7 +162,7 @@ wxString pgEventTrigger::GetSql(ctlTree *browser)
if (sql.IsNull() && (this->eventTriggerFunction))
{
sql = wxT("-- Event Trigger: ") + qtIdent(GetName()) + wxT(" on database ")
+ qtIdent(((pgCollection *)browser->GetObject(browser->GetSelection()))->GetDatabase()->GetName()) + wxT("\n\n")
+ qtIdent(((pgCollection *)browser->GetObject(GetId()))->GetDatabase()->GetName()) + wxT("\n\n")
+ wxT("-- DROP EVENT TRIGGER ") + qtIdent(GetName()) + wxT(";\n\n");
sql += wxT("CREATE EVENT TRIGGER ") + qtIdent(GetName()) + wxT(" ON ")

2210
utils/diff_match_patch.cc Normal file

File diff suppressed because it is too large Load diff

700
utils/diff_match_patch.h Normal file
View file

@ -0,0 +1,700 @@
/*
* Copyright 2008 Google Inc. All Rights Reserved.
* Author: fraser@google.com (Neil Fraser)
* Author: mikeslemmer@gmail.com (Mike Slemmer)
* Author: quentinfiard@gmail.com (Quentin Fiard)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Diff Match and Patch
* http://code.google.com/p/google-diff-match-patch/
*/
#include "pgadmin3.h"
#ifndef DIFF_MATCH_PATCH_H_
#define DIFF_MATCH_PATCH_H_
#include <list>
#include <regex>
#include <string>
#include <tuple>
#include <unordered_map>
#include <vector>
#pragma once
//#include "targetver.h"
//#include <stdio.h>
#include <time.h>
#include <wchar.h>
#include <algorithm>
#include <codecvt>
#include <deque>
#include <iomanip>
#include <limits>
//#include <locale>
#include <sstream>
#include <unordered_set>
#include <iostream>
//#include <string>
#include <cstdint>
#include <memory>
typedef unsigned short ushort;
/*
* Functions for diff, match and patch.
* Computes the difference between two texts to create a patch.
* Applies the patch onto another text, allowing for errors.
*
* @author fraser@google.com (Neil Fraser)
*
* C++11 port by quentinfiard@gmail.com (Quentin Fiard) based
* on the Qt/C++ port by mikeslemmer@gmail.com (Mike Slemmer).
*
* Here is a trivial sample program which works properly when linked with this
* library:
*
#include <string>
#include "diff_match_patch.h"
int main(int argc, char **argv) {
diff_match_patch dmp;
std::string str1 = "First string in diff";
std::string str2 = "Second string in diff";
auto patch = dmp.patch_toText(dmp.patch_make(str1, str2));
auto out = dmp.patch_apply(dmp.patch_fromText(patch), str1);
std::string strResult = out.first;
// here, strResult will equal str2 above.
return strResult != str2;
}
*/
/**-
* The data structure representing a diff is a Linked list of Diff objects:
* {Diff(Operation.DELETE, "Hello"), Diff(Operation.INSERT, "Goodbye"),
* Diff(Operation.EQUAL, " world.")}
* which means: delete "Hello", add "Goodbye" and keep " world."
*/
#if defined(DELETE)
#undef DELETE
#endif // DUMMYSTRUCTNAME
enum Operation { DELETE, INSERT, EQUAL };
/**
* Class representing one diff operation.
*/
class Diff {
public:
Operation operation;
// One of: INSERT, DELETE or EQUAL.
std::wstring text;
// The text associated with this diff operation.
/**
* Constructor. Initializes the diff with the provided values.
* @param operation One of INSERT, DELETE or EQUAL.
* @param text The text being applied.
*/
Diff(Operation _operation, const std::wstring &_text);
Diff();
inline bool isNull() const;
std::wstring toString() const;
bool operator==(const Diff &d) const;
bool operator!=(const Diff &d) const;
static std::wstring strOperation(Operation op);
};
/**
* Class representing one patch operation.
*/
class Patch {
public:
std::list<Diff> diffs;
std::size_t start1;
std::size_t start2;
std::size_t size1;
std::size_t size2;
/**
* Constructor. Initializes with an empty list of diffs.
*/
Patch();
bool isNull() const;
std::wstring toString() const;
};
/**
* Class containing the diff, match and patch methods.
* Also contains the behaviour settings.
*/
class diff_match_patch {
friend class TestableDiffMatchPatch;
public:
// Defaults.
// Set these on your diff_match_patch instance to override the defaults.
// Number of seconds to map a diff before giving up (0 for infinity).
float Diff_Timeout;
// Cost of an empty edit operation in terms of edit characters.
short Diff_EditCost;
// At what point is no match declared (0.0 = perfection, 1.0 = very loose).
float Match_Threshold;
// How far to search for a match (0 = exact location, 1000+ = broad match).
// A match this many characters away from the expected location will add
// 1.0 to the score (0.0 is a perfect match).
int Match_Distance;
// When deleting a large block of text (over ~64 characters), how close does
// the contents have to match the expected contents. (0.0 = perfection,
// 1.0 = very loose). Note that Match_Threshold controls how closely the
// end points of a delete need to match.
float Patch_DeleteThreshold;
// Chunk size for context size.
short Patch_Margin;
// The number of bits in an int.
short Match_MaxBits;
private:
// Define some regex patterns for matching boundaries.
static std::wregex BLANKLINEEND;
static std::wregex BLANKLINESTART;
public:
diff_match_patch();
// DIFF FUNCTIONS
/**
* Find the differences between two texts.
* Run a faster slightly less optimal diff.
* This method allows the 'checklines' of diff_main() to be optional.
* Most of the time checklines is wanted, so default to true.
* @param text1 Old string to be diffed.
* @param text2 New string to be diffed.
* @return Linked List of Diff objects.
*/
std::list<Diff> diff_main(const std::wstring &text1,
const std::wstring &text2);
/**
* Find the differences between two texts.
* @param text1 Old string to be diffed.
* @param text2 New string to be diffed.
* @param checklines Speedup flag. If false, then don't run a
* line-level diff first to identify the changed areas.
* If true, then run a faster slightly less optimal diff.
* @return Linked List of Diff objects.
*/
std::list<Diff> diff_main(const std::wstring &text1,
const std::wstring &text2, bool checklines);
/**
* Find the differences between two texts. Simplifies the problem by
* stripping any common prefix or suffix off the texts before diffing.
* @param text1 Old string to be diffed.
* @param text2 New string to be diffed.
* @param checklines Speedup flag. If false, then don't run a
* line-level diff first to identify the changed areas.
* If true, then run a faster slightly less optimal diff.
* @param deadline Time when the diff should be complete by. Used
* internally for recursive calls. Users should set DiffTimeout instead.
* @return Linked List of Diff objects.
*/
private:
std::list<Diff> diff_main(const std::wstring &text1,
const std::wstring &text2, bool checklines,
clock_t deadline);
/**
* Find the differences between two texts. Assumes that the texts do not
* have any common prefix or suffix.
* @param text1 Old string to be diffed.
* @param text2 New string to be diffed.
* @param checklines Speedup flag. If false, then don't run a
* line-level diff first to identify the changed areas.
* If true, then run a faster slightly less optimal diff.
* @param deadline Time when the diff should be complete by.
* @return Linked List of Diff objects.
*/
private:
std::list<Diff> diff_compute(std::wstring text1, std::wstring text2,
bool checklines, clock_t deadline);
/**
* Do a quick line-level diff on both strings, then rediff the parts for
* greater accuracy.
* This speedup can produce non-minimal diffs.
* @param text1 Old string to be diffed.
* @param text2 New string to be diffed.
* @param deadline Time when the diff should be complete by.
* @return Linked List of Diff objects.
*/
private:
std::list<Diff> diff_lineMode(std::wstring text1, std::wstring text2,
clock_t deadline);
/**
* Find the 'middle snake' of a diff, split the problem in two
* and return the recursively constructed diff.
* See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
* @param text1 Old string to be diffed.
* @param text2 New string to be diffed.
* @return Linked List of Diff objects.
*/
protected:
std::list<Diff> diff_bisect(const std::wstring &text1,
const std::wstring &text2, clock_t deadline);
/**
* Given the location of the 'middle snake', split the diff in two parts
* and recurse.
* @param text1 Old string to be diffed.
* @param text2 New string to be diffed.
* @param x Index of split point in text1.
* @param y Index of split point in text2.
* @param deadline Time at which to bail if not yet complete.
* @return LinkedList of Diff objects.
*/
private:
std::list<Diff> diff_bisectSplit(const std::wstring &text1,
const std::wstring &text2, std::size_t x,
std::size_t y, clock_t deadline);
/**
* Split two texts into a list of strings. Reduce the texts to a string of
* hashes where each Unicode character represents one line.
* @param text1 First string.
* @param text2 Second string.
* @return Three element Object array, containing the encoded text1, the
* encoded text2 and the List of unique strings. The zeroth element
* of the List of unique strings is intentionally blank.
*/
protected:
std::tuple<std::wstring, std::wstring, std::vector<std::wstring> >
diff_linesToChars(const std::wstring &text1,
const std::wstring &text2) const; // return elems 0
// and 1 are
// std::wstring, elem 2
// is std::vector<std::wstring>
/**
* Split a text into a list of strings. Reduce the texts to a string of
* hashes where each Unicode character represents one line.
* @param text String to encode.
* @param lineArray List of unique strings.
* @param lineHash Map of strings to indices.
* @return Encoded string.
*/
private:
std::wstring diff_linesToCharsMunge(
const std::wstring &text, std::vector<std::wstring> &lineArray,
std::unordered_map<std::wstring, std::size_t> &lineHash) const;
/**
* Rehydrate the text in a diff from a string of line hashes to real lines of
* text.
* @param diffs LinkedList of Diff objects.
* @param lineArray List of unique strings.
*/
private:
void diff_charsToLines(std::list<Diff> &diffs,
const std::vector<std::wstring> &lineArray);
/**
* Determine the common prefix of two strings.
* @param text1 First string.
* @param text2 Second string.
* @return The number of characters common to the start of each string.
*/
public:
std::size_t diff_commonPrefix(const std::wstring &text1,
const std::wstring &text2);
/**
* Determine the common suffix of two strings.
* @param text1 First string.
* @param text2 Second string.
* @return The number of characters common to the end of each string.
*/
public:
std::size_t diff_commonSuffix(const std::wstring &text1,
const std::wstring &text2);
/**
* Determine if the suffix of one string is the prefix of another.
* @param text1 First string.
* @param text2 Second string.
* @return The number of characters common to the end of the first
* string and the start of the second string.
*/
protected:
std::size_t diff_commonOverlap(const std::wstring &text1,
const std::wstring &text2);
/**
* Do the two texts share a substring which is at least half the size of
* the longer text?
* This speedup can produce non-minimal diffs.
* @param text1 First string.
* @param text2 Second string.
* @return Five element String array, containing the prefix of text1, the
* suffix of text1, the prefix of text2, the suffix of text2 and the
* common middle. Or null if there was no match.
*/
protected:
std::vector<std::wstring> diff_halfMatch(const std::wstring &text1,
const std::wstring &text2);
/**
* Does a substring of shorttext exist within longtext such that the
* substring is at least half the size of longtext?
* @param longtext Longer string.
* @param shorttext Shorter string.
* @param i Start index of quarter size substring within longtext.
* @return Five element String array, containing the prefix of longtext, the
* suffix of longtext, the prefix of shorttext, the suffix of shorttext
* and the common middle. Or null if there was no match.
*/
private:
std::vector<std::wstring> diff_halfMatchI(const std::wstring &longtext,
const std::wstring &shorttext,
std::size_t i);
/**
* Reduce the number of edits by eliminating semantically trivial equalities.
* @param diffs LinkedList of Diff objects.
*/
public:
void diff_cleanupSemantic(std::list<Diff> &diffs);
/**
* Look for single edits surrounded on both sides by equalities
* which can be shifted sideways to align the edit to a word boundary.
* e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.
* @param diffs LinkedList of Diff objects.
*/
public:
void diff_cleanupSemanticLossless(std::list<Diff> &diffs);
/**
* Given two strings, compute a score representing whether the internal
* boundary falls on logical boundaries.
* Scores range from 6 (best) to 0 (worst).
* @param one First string.
* @param two Second string.
* @return The score.
*/
private:
int diff_cleanupSemanticScore(const std::wstring &one,
const std::wstring &two);
/**
* Reduce the number of edits by eliminating operationally trivial equalities.
* @param diffs LinkedList of Diff objects.
*/
public:
void diff_cleanupEfficiency(std::list<Diff> &diffs);
/**
* Reorder and merge like edit sections. Merge equalities.
* Any edit section can move as long as it doesn't cross an equality.
* @param diffs LinkedList of Diff objects.
*/
public:
void diff_cleanupMerge(std::list<Diff> &diffs);
/**
* loc is a location in text1, compute and return the equivalent location in
* text2.
* e.g. "The cat" vs "The big cat", 1->1, 5->8
* @param diffs LinkedList of Diff objects.
* @param loc Location within text1.
* @return Location within text2.
*/
public:
std::size_t diff_xIndex(const std::list<Diff> &diffs, std::size_t loc);
/**
* Convert a Diff list into a pretty HTML report.
* @param diffs LinkedList of Diff objects.
* @return HTML representation.
*/
public:
std::string diff_prettyHtml(const std::list<Diff> &diffs);
std::wstring diff_widePrettyHtml(const std::list<Diff> &diffs);
/**
* Compute and return the source text (all equalities and deletions).
* @param diffs LinkedList of Diff objects.
* @return Source text.
*/
public:
std::string diff_text1(const std::list<Diff> &diffs);
std::wstring diff_wideText1(const std::list<Diff> &diffs);
/**
* Compute and return the destination text (all equalities and insertions).
* @param diffs LinkedList of Diff objects.
* @return Destination text.
*/
public:
std::string diff_text2(const std::list<Diff> &diffs);
std::wstring diff_wideText2(const std::list<Diff> &diffs);
/**
* Compute the Levenshtein distance; the number of inserted, deleted or
* substituted characters.
* @param diffs LinkedList of Diff objects.
* @return Number of changes.
*/
public:
std::size_t diff_levenshtein(const std::list<Diff> &diffs);
/**
* Crush the diff into an encoded string which describes the operations
* required to transform text1 into text2.
* E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'.
* Operations are tab-separated. Inserted text is escaped using %xx notation.
* @param diffs Array of diff tuples.
* @return Delta text.
*/
public:
std::string diff_toDelta(const std::list<Diff> &diffs);
std::wstring diff_toWideDelta(const std::list<Diff> &diffs);
/**
* Given the original text1, and an encoded string which describes the
* operations required to transform text1 into text2, compute the full diff.
* @param text1 Source string for the diff.
* @param delta Delta text.
* @return Array of diff tuples or null if invalid.
* @throws std::wstring If invalid input.
*/
public:
std::list<Diff> diff_fromDelta(const std::string &text1,
const std::string &delta);
std::list<Diff> diff_fromDelta(const std::wstring &text1,
const std::wstring &delta);
// MATCH FUNCTIONS
/**
* Locate the best instance of 'pattern' in 'text' near 'loc'.
* Returns std::wstring::npos if no match found.
* @param text The text to search.
* @param pattern The pattern to search for.
* @param loc The location to search around.
* @return Best match index or std::wstring::npos.
*/
public:
std::size_t match_main(const std::string &text, const std::string &pattern,
std::size_t loc);
std::size_t match_main(const std::wstring &text, const std::wstring &pattern,
std::size_t loc);
/**
* Locate the best instance of 'pattern' in 'text' near 'loc' using the
* Bitap algorithm. Returns std::wstring::npos if no match found.
* @param text The text to search.
* @param pattern The pattern to search for.
* @param loc The location to search around.
* @return Best match index or std::wstring::npos.
*/
protected:
std::size_t match_bitap(const std::wstring &text, const std::wstring &pattern,
std::size_t loc);
/**
* Compute and return the score for a match with e errors and x location.
* @param e Number of errors in match.
* @param x Location of match.
* @param loc Expected location of match.
* @param pattern Pattern being sought.
* @return Overall score for match (0.0 = good, 1.0 = bad).
*/
private:
double match_bitapScore(std::size_t e, std::size_t x, std::size_t loc,
const std::wstring &pattern);
/**
* Initialise the alphabet for the Bitap algorithm.
* @param pattern The text to encode.
* @return Hash of character locations.
*/
protected:
std::unordered_map<wchar_t, std::size_t> match_alphabet(
const std::wstring &pattern);
// PATCH FUNCTIONS
/**
* Increase the context until it is unique,
* but don't let the pattern expand beyond Match_MaxBits.
* @param patch The patch to grow.
* @param text Source text.
*/
protected:
void patch_addContext(Patch &patch, const std::wstring &text);
/**
* Compute a list of patches to turn text1 into text2.
* A set of diffs will be computed.
* @param text1 Old text.
* @param text2 New text.
* @return LinkedList of Patch objects.
*/
public:
std::list<Patch> patch_make(const std::wstring &text1,
const std::wstring &text2);
std::list<Patch> patch_make(const std::string &text1,
const std::string &text2);
/**
* Compute a list of patches to turn text1 into text2.
* text1 will be derived from the provided diffs.
* @param diffs Array of diff tuples for text1 to text2.
* @return LinkedList of Patch objects.
*/
public:
std::list<Patch> patch_make(const std::list<Diff> &diffs);
/**
* Compute a list of patches to turn text1 into text2.
* text2 is ignored, diffs are the delta between text1 and text2.
* @param text1 Old text.
* @param text2 Ignored.
* @param diffs Array of diff tuples for text1 to text2.
* @return LinkedList of Patch objects.
* @deprecated Prefer patch_make(const std::wstring &text1, const
* std::list<Diff>
* &diffs).
*/
public:
std::list<Patch> patch_make(const std::string &text1,
const std::string &text2,
const std::list<Diff> &diffs);
std::list<Patch> patch_make(const std::wstring &text1,
const std::wstring &text2,
const std::list<Diff> &diffs);
/**
* Compute a list of patches to turn text1 into text2.
* text2 is not provided, diffs are the delta between text1 and text2.
* @param text1 Old text.
* @param diffs Array of diff tuples for text1 to text2.
* @return LinkedList of Patch objects.
*/
public:
std::list<Patch> patch_make(const std::string &text1,
const std::list<Diff> &diffs);
std::list<Patch> patch_make(const std::wstring &text1,
const std::list<Diff> &diffs);
/**
* Given an array of patches, return another array that is identical.
* @param patches Array of patch objects.
* @return Array of patch objects.
*/
public:
std::list<Patch> patch_deepCopy(const std::list<Patch> &patches);
/**
* Merge a set of patches onto the text. Return a patched text, as well
* as an array of true/false values indicating which patches were applied.
* @param patches Array of patch objects.
* @param text Old text.
* @return Two element Object array, containing the new text and an array of
* boolean values.
*/
public:
std::pair<std::wstring, std::vector<bool> > patch_apply(
const std::list<Patch> &patches, const std::wstring &text);
std::pair<std::string, std::vector<bool> > patch_apply(
const std::list<Patch> &patches, const std::string &text);
/**
* Add some padding on text start and end so that edges can match something.
* Intended to be called only from within patch_apply.
* @param patches Array of patch objects.
* @return The padding string added to each side.
*/
public:
std::string patch_addPadding(std::list<Patch> &patches);
std::wstring patch_addWidePadding(std::list<Patch> &patches);
/**
* Look through the patches and break up any which are longer than the
* maximum limit of the match algorithm.
* Intended to be called only from within patch_apply.
* @param patches LinkedList of Patch objects.
*/
public:
void patch_splitMax(std::list<Patch> &patches);
/**
* Take a list of patches and return a textual representation.
* @param patches List of Patch objects.
* @return Text representation of patches.
*/
public:
std::string patch_toText(const std::list<Patch> &patches);
std::wstring patch_toWideText(const std::list<Patch> &patches);
/**
* Parse a textual representation of patches and return a List of Patch
* objects.
* @param textline Text representation of patches.
* @return List of Patch objects.
* @throws std::wstring If invalid input.
*/
public:
std::list<Patch> patch_fromText(const std::wstring &textline);
std::list<Patch> patch_fromText(const std::string &textline);
/**
* A safer version of std::wstring.mid(pos). This one returns "" instead of
* null when the postion equals the string size.
* @param str String to take a substring from.
* @param pos Position to start the substring from.
* @return Substring.
*/
private:
static inline std::wstring safeSubStr(const std::wstring &str,
std::size_t pos) {
return (pos >= str.size()) ? std::wstring() : str.substr(pos);
}
/**
* A safer version of std::wstring.mid(pos, len). This one returns "" instead
* of
* null when the postion equals the string size.
* @param str String to take a substring from.
* @param pos Position to start the substring from.
* @param len Length of substring.
* @return Substring.
*/
private:
static inline std::wstring safeSubStr(const std::wstring &str,
std::size_t pos, std::size_t len) {
return (pos == str.size()) ? std::wstring() : str.substr(pos, len);
}
};
#endif // DIFF_MATCH_PATCH_H_