mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 14:15:49 -06:00
Compare add
add support VS2012 dll add compare objects tree
This commit is contained in:
parent
7e10968209
commit
748d9b7950
31 changed files with 3971 additions and 4 deletions
|
|
@ -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.
276
Release_(3.0)/textcompare_report.template
Normal file
276
Release_(3.0)/textcompare_report.template
Normal 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;">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<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>
|
||||
BIN
Release_(3.0)/wxbase30u_net_vc110.dll
Normal file
BIN
Release_(3.0)/wxbase30u_net_vc110.dll
Normal file
Binary file not shown.
Binary file not shown.
BIN
Release_(3.0)/wxbase30u_vc110.dll
Normal file
BIN
Release_(3.0)/wxbase30u_vc110.dll
Normal file
Binary file not shown.
Binary file not shown.
BIN
Release_(3.0)/wxbase30u_xml_vc110.dll
Normal file
BIN
Release_(3.0)/wxbase30u_xml_vc110.dll
Normal file
Binary file not shown.
Binary file not shown.
BIN
Release_(3.0)/wxmsw30u_adv_vc110.dll
Normal file
BIN
Release_(3.0)/wxmsw30u_adv_vc110.dll
Normal file
Binary file not shown.
Binary file not shown.
BIN
Release_(3.0)/wxmsw30u_aui_vc110.dll
Normal file
BIN
Release_(3.0)/wxmsw30u_aui_vc110.dll
Normal file
Binary file not shown.
Binary file not shown.
BIN
Release_(3.0)/wxmsw30u_core_vc110.dll
Normal file
BIN
Release_(3.0)/wxmsw30u_core_vc110.dll
Normal file
Binary file not shown.
Binary file not shown.
BIN
Release_(3.0)/wxmsw30u_html_vc110.dll
Normal file
BIN
Release_(3.0)/wxmsw30u_html_vc110.dll
Normal file
Binary file not shown.
Binary file not shown.
BIN
Release_(3.0)/wxmsw30u_stc_vc110.dll
Normal file
BIN
Release_(3.0)/wxmsw30u_stc_vc110.dll
Normal file
Binary file not shown.
Binary file not shown.
BIN
Release_(3.0)/wxmsw30u_xrc_vc110.dll
Normal file
BIN
Release_(3.0)/wxmsw30u_xrc_vc110.dll
Normal file
Binary file not shown.
Binary file not shown.
|
|
@ -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++];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#ifndef PGADMIN3_H
|
||||
#define PGADMIN3_H
|
||||
#include "../utils/diff_match_patch.h"
|
||||
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ pgCollection::pgCollection(pgaFactory *factory)
|
|||
schema = 0;
|
||||
database = 0;
|
||||
server = 0;
|
||||
projob = 0;
|
||||
}
|
||||
|
||||
bool pgCollection::IsCollectionFor(pgObject *obj)
|
||||
|
|
|
|||
|
|
@ -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
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
700
utils/diff_match_patch.h
Normal 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_
|
||||
Loading…
Add table
Add a link
Reference in a new issue