pgadmin3/frm/frmReport.cpp
lsv 9b4045a94c fix compare report.
При сравнении баз некорректно определялся путь в дереве объектов.
2026-04-30 10:43:43 +05:00

2588 lines
77 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// frmReport.cpp - The report file dialogue
//
//////////////////////////////////////////////////////////////////////////
// App headers
#include "pgAdmin3.h"
#include <wx/file.h>
#include <wx/regex.h>
#include "frm/frmMain.h"
#include "frm/frmReport.h"
#include "utils/sysSettings.h"
#include "utils/misc.h"
#include "ctl/ctlListView.h"
#include "schema/pgObject.h"
#include "schema/pgCollection.h"
#include "schema/pgServer.h"
#include "schema/pgDatabase.h"
#include "schema/pgSchema.h"
#include "schema/pgTable.h"
#include "schema/pgColumn.h"
#include "schema/pgConstraints.h"
#include "agent/pgaJob.h"
#include "schema/pgForeignKey.h"
#include "schema/pgIndexConstraint.h"
#include "schema/pgCheck.h"
#include "utils/utffile.h"
#include <wx/stdpaths.h>
#include "../utils/diff_match_patch.h"
// XML2/XSLT headers
#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>
#define txtTitle CTRL_TEXT("txtTitle")
#define txtNotes CTRL_TEXT("txtNotes")
#define txtHtmlFile CTRL_TEXT("txtHtmlFile")
#define txtXmlFile CTRL_TEXT("txtXmlFile")
#define txtHtmlStylesheet CTRL_TEXT("txtHtmlStylesheet")
#define txtXmlStylesheet CTRL_TEXT("txtXmlStylesheet")
#define btnOK CTRL_BUTTON("wxID_OK")
#define btnFile CTRL_BUTTON("btnFile")
#define btnStylesheet CTRL_BUTTON("btnStylesheet")
#define rbHtml CTRL_RADIOBUTTON("rbHtml")
#define rbXml CTRL_RADIOBUTTON("rbXml")
#define rbHtmlBuiltin CTRL_RADIOBUTTON("rbHtmlBuiltin")
#define rbHtmlEmbed CTRL_RADIOBUTTON("rbHtmlEmbed")
#define rbHtmlLink CTRL_RADIOBUTTON("rbHtmlLink")
#define rbXmlPlain CTRL_RADIOBUTTON("rbXmlPlain")
#define rbXmlLink CTRL_RADIOBUTTON("rbXmlLink")
#define rbXmlProcess CTRL_RADIOBUTTON("rbXmlProcess")
#define chkSql CTRL_CHECKBOX("chkSql")
#define chkBrowser CTRL_CHECKBOX("chkBrowser")
BEGIN_EVENT_TABLE(frmReport, pgDialog)
EVT_RADIOBUTTON(XRCID("rbHtml"), frmReport::OnChange)
EVT_RADIOBUTTON(XRCID("rbXml"), frmReport::OnChange)
EVT_RADIOBUTTON(XRCID("rbHtmlBuiltin"), frmReport::OnChange)
EVT_RADIOBUTTON(XRCID("rbHtmlEmbed"), frmReport::OnChange)
EVT_RADIOBUTTON(XRCID("rbHtmlLink"), frmReport::OnChange)
EVT_RADIOBUTTON(XRCID("rbXmlPlain"), frmReport::OnChange)
EVT_RADIOBUTTON(XRCID("rbXmlLink"), frmReport::OnChange)
EVT_RADIOBUTTON(XRCID("rbXmlProcess"), frmReport::OnChange)
EVT_TEXT(XRCID("txtHtmlFile"), frmReport::OnChange)
EVT_TEXT(XRCID("txtXmlFile"), frmReport::OnChange)
EVT_TEXT(XRCID("txtHtmlStylesheet"), frmReport::OnChange)
EVT_TEXT(XRCID("txtXmlStylesheet"), frmReport::OnChange)
EVT_BUTTON(XRCID("btnFile"), frmReport::OnBrowseFile)
EVT_BUTTON(XRCID("btnStylesheet"), frmReport::OnBrowseStylesheet)
EVT_BUTTON(wxID_HELP, frmReport::OnHelp)
EVT_BUTTON(wxID_OK, frmReport::OnOK)
EVT_BUTTON(wxID_CANCEL, frmReport::OnCancel)
END_EVENT_TABLE()
extern wxString dataDir;
typedef std::vector<double> vectord;
template<typename A, typename B>
std::pair<B, A> flip_pair(const std::pair<A, B>& p)
{
return std::pair<B, A>(p.second, p.first);
}
template<typename A, typename B>
std::multimap<B, A> flip_map(const std::map<A, B>& src)
{
std::multimap<B, A> dst;
std::transform(src.begin(), src.end(), std::inserter(dst, dst.begin()),
flip_pair<A, B>);
return dst;
}
const static wxString names[] = { "Count","Sum","Min","Max","Avg","Mediana","Moda"};
class Stats
{
std::vector<vectord> columns;
std::vector<int> types_col;
std::map<wxString, double> metrics;
int rows = 0;
public:
Stats(int rows, int cols, const std::vector<int> &types) {
for (int x = 0; x < cols; x++) {
vectord arrays(rows);
columns.push_back(arrays);
}
types_col = types;
this->rows = rows;
}
void addValue(int row,int col,double d) {
if (types_col[col] < 1) return;
if (col == 0) {
return; // no use 0 column
}
columns[col][row] = d;
}
int getCountMetrics() {
return 7;
}
void calc() {
metrics.clear();
// Count
{
//wxString k = names[nm];
for (int col = 1; col < columns.size(); col++) {
if (types_col[col] < 1) continue;
vectord elements=columns[col];
//elements.reserve(1000);
double sum = 0.0;
double avg = 0;
double med = 0;
std::size_t n = elements.size();
std::sort(elements.begin(), elements.end());
//std::cout << "max=" << elements.back() << std::endl;
double max = elements.back();
metrics[getKey(names[3], col)] = max; //max
//std::cout << "min=" << elements.front() << std::endl;
metrics[getKey(names[2], col)] = elements.front(); //min
for (auto& x : elements) {
sum += x;
}
if (n == 0) continue;
//std::cout << "avg=" << sum / n << std::endl;
metrics[getKey(names[1], col)] = sum;
avg = sum / n;
metrics[getKey(names[4], col)] = avg;
if (n == 1) {
//std::cout << "med=" << elements[0] << std::endl;
med = elements[0];
}
else if (n % 2 == 0) {
std::size_t i = n / 2;
//std::cout << "med=" << (double)(elements[i] + elements[i - 1]) / 2.0 << std::endl;
med = (elements[i] + elements[i - 1]) / 2.0;
}
else {
//std::cout << "med=" << elements[n / 2] << std::endl;
med = elements[n / 2];
}
metrics[getKey(names[5], col)] = med;
std::set<double> elements_set;
for (auto& x : elements) {
elements_set.insert(x);
}
std::map<double, std::size_t> elements_count;
for (auto& x : elements_set) {
elements_count.insert(std::pair<int, std::size_t>(x, std::count(elements.begin(), elements.end(), x)));
}
std::multimap<std::size_t, double> count_elements;
count_elements = flip_map(elements_count);
//std::cout << "mod: ";
std::size_t count_mod = count_elements.rbegin()->first;
for (auto& x : count_elements) {
if (x.first == count_mod) {
//std::cout << x.second << ", ";
metrics[getKey(names[6], col)] = x.second;
}
else {}
}
//std::cout << "(" << count_mod << ")" << std::endl;
wxString s_count_mod = "(" + NumToStr((long)count_mod) + ")";
}
}
}
wxArrayString GetRowForTable(int numMetric) {
wxArrayString footcols;
wxString name;
bool isEmpty = true;
if (numMetric >= getCountMetrics()) return footcols;
for (int x = 0; x < columns.size();x++) {
if (x == 0) {
// Title
name = names[numMetric];
footcols.Add(name);
}
else {
if (numMetric==0) {
if (x == 1) footcols.Add(NumToStr((long)rows));
else
footcols.Add("");
isEmpty = false;
}
else {
wxString k = getKey(name, x);
wxString v;
auto it = metrics.find(k);
double d;
if (it != metrics.end()) {
d = it->second;
//if
wxString suff;
if (types_col[x] == 2) {
if (d > 1024) {
d = d / 1024;
suff = "GB";
}
else suff = "MB";
}
if (suff.Length() > 0) v.Printf(wxT("%.2lf %s"), d, suff);
else v.Printf(wxT("%.2lf"), d);
} else {
}
footcols.Add(v);
if (v.Length() > 0) isEmpty = false;
}
}
}
if (isEmpty) footcols.Clear();
return footcols;
}
wxString getKey(const wxString& name, int col) {
return wxString::Format("%s_%d", name,col);
}
};
frmReport::frmReport(wxWindow *p)
{
parent = p;
header = wxT("");
SetFont(settings->GetSystemFont());
LoadResource(p, wxT("frmReport"));
// Icon
appearanceFactory->SetIcons(this);
RestorePosition();
btnOK->Disable();
wxString val;
bool bVal;
// Output format
settings->Read(wxT("Reports/ReportFormat"), &val, wxT("h"));
if (val == wxT("x"))
{
rbHtml->SetValue(false);
rbXml->SetValue(true);
}
else
{
rbHtml->SetValue(true);
rbXml->SetValue(false);
}
// HTML Stylesheet
settings->Read(wxT("Reports/HtmlStylesheetMode"), &val, wxT("b"));
if (val == wxT("e"))
{
rbHtmlBuiltin->SetValue(false);
rbHtmlEmbed->SetValue(true);
rbHtmlLink->SetValue(false);
}
else if (val == wxT("l"))
{
rbHtmlBuiltin->SetValue(false);
rbHtmlEmbed->SetValue(false);
rbHtmlLink->SetValue(true);
}
else
{
rbHtmlBuiltin->SetValue(true);
rbHtmlEmbed->SetValue(false);
rbHtmlLink->SetValue(false);
}
// XML Stylesheet
settings->Read(wxT("Reports/XmlStylesheetMode"), &val, wxT("p"));
if (val == wxT("l"))
{
rbXmlPlain->SetValue(false);
rbXmlLink->SetValue(true);
rbXmlProcess->SetValue(false);
}
else if (val == wxT("r"))
{
rbXmlPlain->SetValue(false);
rbXmlLink->SetValue(false);
rbXmlProcess->SetValue(true);
}
else
{
rbXmlPlain->SetValue(true);
rbXmlLink->SetValue(false);
rbXmlProcess->SetValue(false);
}
// Default values
settings->Read(wxT("Reports/LastNotes"), &val, wxT(""));
txtNotes->SetValue(val);
settings->Read(wxT("Reports/LastHtmlStylesheet"), &val, wxEmptyString);
txtHtmlStylesheet->SetValue(val);
settings->Read(wxT("Reports/LastXmlStylesheet"), &val, wxEmptyString);
txtXmlStylesheet->SetValue(val);
settings->Read(wxT("Reports/LastHtmlFile"), &val, wxEmptyString);
txtHtmlFile->SetValue(val);
settings->Read(wxT("Reports/LastXmlFile"), &val, wxEmptyString);
txtXmlFile->SetValue(val);
settings->Read(wxT("Reports/IncludeSQL"), &bVal, true);
chkSql->SetValue(bVal);
chkSql->Disable();
settings->Read(wxT("Reports/OpenInBrowser"), &bVal, true);
chkBrowser->SetValue(bVal);
wxCommandEvent ev;
OnChange(ev);
txtTitle->SetFocus();
}
frmReport::~frmReport()
{
SavePosition();
}
void frmReport::OnHelp(wxCommandEvent &ev)
{
DisplayHelp(wxT("reports"), HELP_PGADMIN);
}
void frmReport::OnChange(wxCommandEvent &ev)
{
bool enable = true;
if (rbHtml->GetValue())
{
// Show/hide the appropriate controls
rbHtmlBuiltin->Show(true);
rbHtmlEmbed->Show(true);
rbHtmlLink->Show(true);
txtHtmlStylesheet->Show(true);
txtHtmlFile->Show(true);
rbXmlPlain->Show(false);
rbXmlLink->Show(false);
rbXmlProcess->Show(false);
txtXmlStylesheet->Show(false);
txtXmlFile->Show(false);
// Enable/disable as appropriate
if (txtHtmlFile->GetValue().IsEmpty())
enable = false;
if (rbHtmlBuiltin->GetValue())
{
txtHtmlStylesheet->Disable();
btnStylesheet->Disable();
}
else
{
txtHtmlStylesheet->Enable();
btnStylesheet->Enable();
}
if (rbHtmlEmbed->GetValue())
{
if (!wxFile::Exists(txtHtmlStylesheet->GetValue()))
enable = false;
}
}
else
{
// Show/hide the appropriate controls
rbHtmlBuiltin->Show(false);
rbHtmlEmbed->Show(false);
rbHtmlLink->Show(false);
txtHtmlStylesheet->Show(false);
txtHtmlFile->Show(false);
rbXmlPlain->Show(true);
rbXmlLink->Show(true);
rbXmlProcess->Show(true);
txtXmlStylesheet->Show(true);
txtXmlFile->Show(true);
// Enable/disable as appropriate
if (txtXmlFile->GetValue().IsEmpty())
enable = false;
if (rbXmlPlain->GetValue())
{
txtXmlStylesheet->Disable();
btnStylesheet->Disable();
}
else
{
txtXmlStylesheet->Enable();
btnStylesheet->Enable();
}
if (rbXmlProcess->GetValue())
{
if (!wxFile::Exists(txtXmlStylesheet->GetValue()))
enable = false;
}
}
btnOK->Enable(enable);
}
void frmReport::OnOK(wxCommandEvent &ev)
{
wxString filename;
if (rbHtml->GetValue())
filename = txtHtmlFile->GetValue();
else
filename = txtXmlFile->GetValue();
wxFileName fn(filename);
fn.MakeAbsolute();
// Check if the file exsits, and if so, whether to overwrite it
if (wxFileExists(fn.GetFullPath()))
{
wxString msg;
msg.Printf(_("The file: \n\n%s\n\nalready exists. Do you want to overwrite it?"), fn.GetFullPath().c_str());
if (wxMessageBox(msg, _("Overwrite file?"), wxYES_NO | wxICON_QUESTION) != wxYES)
{
if (rbHtml->GetValue())
txtHtmlFile->SetFocus();
else
txtXmlFile->SetFocus();
return;
}
}
// Yield, to allow the messagebox to go
wxTheApp->Yield(true);
// Add the title, notes and SQL
if (txtTitle->GetValue() != wxT(""))
XmlAddHeaderValue(wxT("title"), txtTitle->GetValue());
if (txtNotes->GetValue() != wxT(""))
XmlAddHeaderValue(wxT("notes"), txtNotes->GetValue());
// Generate the report data
wxString report;
if (rbHtml->GetValue())
{
if (rbHtmlBuiltin->GetValue())
{
wxString xml = GetXmlReport(wxEmptyString);
wxString xsl = GetDefaultXsl(GetEmbeddedCss(GetDefaultCss()));
report = XslProcessReport(xml, xsl);
}
else if (rbHtmlEmbed->GetValue())
{
wxString xml = GetXmlReport(wxEmptyString);
wxString css = FileRead(txtHtmlStylesheet->GetValue());
if (css.IsEmpty())
{
wxLogError(_("No stylesheet data could be read from the file %s: Errcode=%d."), txtHtmlStylesheet->GetValue().c_str(), wxSysErrorCode());
return;
}
wxString xsl = GetDefaultXsl(GetEmbeddedCss(css));
report = XslProcessReport(xml, xsl);
}
else
{
wxString xml = GetXmlReport(wxEmptyString);
wxString xsl = GetDefaultXsl(GetCssLink(txtHtmlStylesheet->GetValue()));
report = XslProcessReport(xml, xsl);
}
}
else
{
if (rbXmlPlain->GetValue())
{
report = GetXmlReport(wxEmptyString);
}
else if (rbXmlLink->GetValue())
{
report = GetXmlReport(txtXmlStylesheet->GetValue());
}
else
{
wxString xml = GetXmlReport(wxEmptyString);
wxString xsl = FileRead(txtXmlStylesheet->GetValue());
if (xsl.IsEmpty())
{
wxLogError(_("No stylesheet data could be read from the file %s: Errcode=%d."), txtXmlStylesheet->GetValue().c_str(), wxSysErrorCode());
return;
}
report = XslProcessReport(xml, xsl);
}
}
// If report is empty, an error must have occurred
if (report.IsEmpty())
return;
// Save it to disk
wxFile file(fn.GetFullPath(), wxFile::write);
if (!file.IsOpened())
{
wxLogError(_("Failed to open file %s."), fn.GetFullPath().c_str());
return;
}
file.Write(report, wxConvUTF8);
file.Close();
// Open the file in the default browser if required
if (chkBrowser->GetValue())
#ifdef __WXMSW__
wxLaunchDefaultBrowser(fn.GetFullPath());
#else
wxLaunchDefaultBrowser(wxT("file://") + fn.GetFullPath());
#endif
// Save the settings for next time round
settings->Write(wxT("Reports/LastNotes"), txtNotes->GetValue());
if (rbHtml->GetValue())
settings->Write(wxT("Reports/ReportFormat"), wxT("h"));
else
settings->Write(wxT("Reports/ReportFormat"), wxT("x"));
if (rbHtmlBuiltin->GetValue())
settings->Write(wxT("Reports/HtmlStylesheetMode"), wxT("b"));
else if (rbHtmlEmbed->GetValue())
settings->Write(wxT("Reports/HtmlStylesheetMode"), wxT("e"));
else
settings->Write(wxT("Reports/HtmlStylesheetMode"), wxT("l"));
if (rbXmlPlain->GetValue())
settings->Write(wxT("Reports/XmlStylesheetMode"), wxT("p"));
else if (rbXmlLink->GetValue())
settings->Write(wxT("Reports/XmlStylesheetMode"), wxT("l"));
else
settings->Write(wxT("Reports/XmlStylesheetMode"), wxT("r"));
settings->Write(wxT("Reports/LastHtmlStylesheet"), txtHtmlStylesheet->GetValue());
settings->Write(wxT("Reports/LastXmlStylesheet"), txtXmlStylesheet->GetValue());
settings->Write(wxT("Reports/LastHtmlFile"), txtHtmlFile->GetValue());
settings->Write(wxT("Reports/LastXmlFile"), txtXmlFile->GetValue());
settings->WriteBool(wxT("Reports/IncludeSQL"), chkSql->GetValue());
settings->WriteBool(wxT("Reports/OpenInBrowser"), chkBrowser->GetValue());
// Now go away
if (IsModal())
EndModal(wxID_OK);
else
Destroy();
}
void frmReport::OnCancel(wxCommandEvent &ev)
{
if (IsModal())
EndModal(wxID_CANCEL);
else
Destroy();
}
void frmReport::OnBrowseStylesheet(wxCommandEvent &ev)
{
if (rbHtml->GetValue())
{
wxString def = txtHtmlStylesheet->GetValue();
if (!wxFile::Exists(def))
def.Empty();
#ifdef __WXMSW__
wxFileDialog file(this, _("Select stylesheet filename"), wxGetHomeDir(), def, _("HTML Stylesheet files (*.css)|*.css|All files (*.*)|*.*"), wxFD_OPEN);
#else
wxFileDialog file(this, _("Select stylesheet filename"), wxGetHomeDir(), def, _("HTML Stylesheet files (*.css)|*.css|All files (*)|*"), wxFD_OPEN);
#endif
if (file.ShowModal() == wxID_OK)
{
txtHtmlStylesheet->SetValue(file.GetPath());
OnChange(ev);
}
}
else
{
wxString def = txtXmlStylesheet->GetValue();
if (!wxFile::Exists(def))
def.Empty();
#ifdef __WXMSW__
wxFileDialog file(this, _("Select stylesheet filename"), wxGetHomeDir(), def, _("XML Stylesheet files (*.xsl)|*.xsl|All files (*.*)|*.*"), wxFD_OPEN);
#else
wxFileDialog file(this, _("Select stylesheet filename"), wxGetHomeDir(), def, _("XML Stylesheet files (*.xsl)|*.xsl|All files (*)|*"), wxFD_OPEN);
#endif
if (file.ShowModal() == wxID_OK)
{
txtXmlStylesheet->SetValue(file.GetPath());
OnChange(ev);
}
}
}
void frmReport::OnBrowseFile(wxCommandEvent &ev)
{
if (rbHtml->GetValue())
{
#ifdef __WXMSW__
wxFileDialog file(this, _("Select output filename"), wxGetHomeDir(), txtHtmlFile->GetValue(),
_("HTML files (*.html)|*.html|All files (*.*)|*.*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
#else
wxFileDialog file(this, _("Select output filename"), wxGetHomeDir(), txtHtmlFile->GetValue(),
_("HTML files (*.html)|*.html|All files (*)|*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
#endif
if (file.ShowModal() == wxID_OK)
{
txtHtmlFile->SetValue(file.GetPath());
OnChange(ev);
}
}
else
{
#ifdef __WXMSW__
wxFileDialog file(this, _("Select output filename"), wxGetHomeDir(), txtXmlFile->GetValue(),
_("XML files (*.xml)|*.xml|All files (*.*)|*.*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
#else
wxFileDialog file(this, _("Select output filename"), wxGetHomeDir(), txtXmlFile->GetValue(),
_("XML files (*.xml)|*.xml|All files (*)|*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
#endif
if (file.ShowModal() == wxID_OK)
{
txtXmlFile->SetValue(file.GetPath());
OnChange(ev);
}
}
}
void frmReport::SetReportTitle(const wxString &t)
{
txtTitle->SetValue(t);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// START STYLESHEET FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
wxString frmReport::GetCssLink(const wxString &file)
{
wxString data;
data = wxT(" <link rel=\"stylesheet\" href=\"") + file + wxT("\" type=\"text/css\" />\n");
return data;
}
wxString frmReport::GetEmbeddedCss(const wxString &css)
{
wxString data;
data = wxT(" <style type=\"text/css\">\n");
data += css;
data += wxT(" </style>\n");
return data;
}
const wxString frmReport::GetDefaultCss()
{
wxString data;
data = wxT(" body { font-family: verdana, helvetica, sans-serif; margin: 0px; padding: 0; }\n")
wxT(" h1 { font-weight: bold; font-size: 150%; border-bottom-style: solid; border-bottom-width: 2px; margin-top: 0px; padding-bottom: 0.5ex; color: #eeeeee; overflow: hidden; text-overflow: ellipsis; }\n")
wxT(" h2 { font-size: 130%; padding-bottom: 0.5ex; color: ") + appearanceFactory->GetReportKeyColour().GetAsString(wxC2S_HTML_SYNTAX) + wxT("; border-bottom-style: solid; border-bottom-width: 2px; }\n")
wxT(" h3 { font-size: 110%; padding-bottom: 0.5ex; color: #000000; }\n")
wxT(" th { text-align: left; background-color: ") + appearanceFactory->GetReportKeyColour().GetAsString(wxC2S_HTML_SYNTAX) + wxT("; color: #eeeeee; }\n")
wxT(" .tableFixHead {\n")
wxT(" overflow-y: auto; /* make the table scrollable if height is more than 200 px */\n")
wxT(" height: 300px; /* gives an initial height of 200px to the table */\n")
wxT(" }\n")
wxT(" .tableFixHead thead th {\n")
wxT(" position: sticky; /* make the table heads sticky */\n")
wxT(" top: 0px; /* table head will be placed from the top of the table and sticks to it */\n")
wxT(" }\n")
wxT(" #ReportHeader { padding: 10px; background-color: ") + appearanceFactory->GetReportKeyColour().GetAsString(wxC2S_HTML_SYNTAX) + wxT("; color: #eeeeee; border-bottom-style: solid; border-bottom-width: 2px; border-color: #999999; }\n")
wxT(" #ReportHeader th { width: 25%; white-space: nowrap; vertical-align: top; }\n")
wxT(" #ReportHeader td { vertical-align: top; color: #eeeeee; }\n")
wxT(" #ReportNotes { padding: 10px; background-color: #eeeeee; font-size: 80%; border-bottom-style: solid; border-bottom-width: 2px; border-color: #999999; }\n")
wxT(" .ReportSQL { margin-bottom: 10px; padding: 10px; display: block; background-color: #eeeeee; font-family: monospace; }\n")
wxT(" #ReportDetails { margin-left: 10px; margin-right: 10px; margin-bottom: 10px; }\n")
wxT(" #ReportDetails td, th { font-size: 80%; margin-left: 2px; margin-right: 2px; }\n")
wxT(" #ReportDetails th { border-bottom-color: #777777; border-bottom-style: solid; border-bottom-width: 2px; }\n")
wxT(" .ReportDetailsOddDataRow { background-color: #dddddd; }\n")
wxT(" .ReportDetailsEvenDataRow { background-color: #eeeeee; }\n")
wxT(" .ReportDetailsFootDataRow { font-weight: bold; background-color: #cccccc; }\n")
wxT(" .ReportTableHeaderCell { background-color: #dddddd; color: ") + appearanceFactory->GetReportKeyColour().GetAsString(wxC2S_HTML_SYNTAX) + wxT("; vertical-align: top; font-size: 80%; white-space: nowrap; }\n")
wxT(" .ReportTableValueCell { vertical-align: top; font-size: 80%; white-space: nowrap; padding: 2px; }\n")
wxT(" .ReportTableInfo { font-size: 80%; font-style: italic; }\n")
wxT(" #ReportFooter { font-weight: bold; font-size: 80%; text-align: right; background-color: ") + appearanceFactory->GetReportKeyColour().GetAsString(wxC2S_HTML_SYNTAX) + wxT("; color: #eeeeee; margin-top: 10px; padding: 2px; border-bottom-style: solid; border-bottom-width: 2px; border-top-style: solid; border-top-width: 2px; border-color: #999999; }\n")
wxT(" #ReportFooter a { color: #ffffff; text-decoration: none; }\n");
return data;
}
wxString frmReport::GetDefaultXsl(const wxString &css)
{
wxString data;
data = wxT("<?xml version=\"1.0\"?>\n")
wxT("<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n")
wxT("<xsl:output method=\"xml\" doctype-system=\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\" doctype-public=\"-//W3C//DTD XHTML 1.0 Transitional//EN\" indent=\"yes\" encoding=\"utf-8\" />\n")
wxT("<xsl:template match=\"/report\">\n")
wxT("\n")
wxT("<html>\n")
wxT(" <head>\n")
wxT(" <xsl:if test=\"header/title != ''\">\n")
wxT(" <title><xsl:value-of select=\"header/title\" /></title>\n")
wxT(" </xsl:if>\n")
wxT(" <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n");
data += css;
data += wxT(" </head>\n")
wxT("\n")
wxT(" <body>\n")
wxT(" <div id=\"ReportHeader\">\n")
wxT("\n")
wxT(" <xsl:if test=\"header/title != ''\">\n")
wxT(" <h1><xsl:value-of select=\"header/title\" /></h1>\n")
wxT(" </xsl:if>\n")
wxT("\n")
wxT(" <xsl:if test=\"header/generated != ''\">\n")
wxT(" <b>");
data += _("Generated");
data += wxT(": </b><xsl:value-of select=\"header/generated\" /><br />\n")
wxT(" </xsl:if>\n")
wxT("\n")
wxT(" <xsl:if test=\"header/server != ''\">\n")
wxT(" <b>");
data += _("Server");
data += wxT(": </b><xsl:value-of select=\"header/server\" /><br />\n")
wxT(" </xsl:if>\n")
wxT("\n")
wxT(" <xsl:if test=\"header/database != ''\">\n")
wxT(" <b>");
data += _("Database");
data += wxT(": </b><xsl:value-of select=\"header/database\" /><br />\n")
wxT(" </xsl:if>\n")
wxT("\n")
wxT(" <xsl:if test=\"header/catalog != ''\">\n")
wxT(" <b>");
data += _("Catalog");
data += wxT(": </b><xsl:value-of select=\"header/catalog\" /><br />\n")
wxT(" </xsl:if>\n")
wxT("\n")
wxT(" <xsl:if test=\"header/schema != ''\">\n")
wxT(" <b>");
data += _("Schema");
data += wxT(": </b><xsl:value-of select=\"header/schema\" /><br />\n")
wxT(" </xsl:if>\n")
wxT("\n")
wxT(" <xsl:if test=\"header/table != ''\">\n")
wxT(" <b>");
data += _("Table");
data += wxT(": </b><xsl:value-of select=\"header/table\" /><br />\n")
wxT(" </xsl:if>\n")
wxT("\n")
wxT(" <xsl:if test=\"header/job != ''\">\n")
wxT(" <b>");
data += _("Job");
data += wxT(": </b><xsl:value-of select=\"header/job\" /><br />\n")
wxT(" </xsl:if>\n")
wxT("\n")
wxT(" </div>\n")
wxT("\n")
wxT(" <xsl:if test=\"header/notes != ''\">\n")
wxT(" <div id=\"ReportNotes\">\n")
wxT(" <b>");
data += _("Notes");
data += wxT(": </b><br /><br />\n")
wxT(" <xsl:call-template name=\"substitute\">\n")
wxT(" <xsl:with-param name=\"string\" select=\"header/notes\" />\n")
wxT(" </xsl:call-template>\n")
wxT(" </div>\n")
wxT(" </xsl:if>\n")
wxT("\n")
wxT(" <div id=\"ReportDetails\">\n")
wxT(" <xsl:apply-templates select=\"section\" >\n")
wxT(" <xsl:sort select=\"@number\" data-type=\"number\" order=\"ascending\" />\n")
wxT(" </xsl:apply-templates>\n")
wxT(" </div>\n")
wxT("\n")
wxT(" <div id=\"ReportFooter\">\n");
data += _("Report generated by");
data += wxT(" <a href=\"");
data += HtmlEntities(appearanceFactory->GetWebsiteUrl());
data += wxT("\">");
data += HtmlEntities(appearanceFactory->GetLongAppName());
data += wxT("</a>\n")
wxT(" </div>\n")
wxT("\n")
wxT(" <br />\n")
wxT(" </body>\n")
wxT("<script>\n")
wxT("<xsl:comment><xsl:text disable-output-escaping=\"yes\"> <![CDATA[\n")
wxT("function adjustTableBodyHeight() {\n")
wxT(" var cnt=document.querySelectorAll('.tableFixHead').length;\n")
wxT(" if (cnt!=1) {\n")
wxT(" [...document.querySelectorAll('.tableFixHead')].forEach(el => { el.classList.remove('tableFixHead'); });\n")
wxT(" } else {\n")
wxT(" \n")
wxT(" var tableBody = document.querySelector('.tableFixHead');\n")
wxT(" var rf=document.getElementById('ReportFooter');\n")
wxT(" var wh=window.innerHeight;\n")
wxT(" var availableHeight = wh - tableBody.getBoundingClientRect().top - rf.getBoundingClientRect().height;\n")
wxT(" tableBody.style.height = Math.max(availableHeight, 100) + 'px';\n")
wxT(" }\n")
wxT("}\n")
wxT("window.onload = adjustTableBodyHeight;\n")
wxT("window.onresize = adjustTableBodyHeight;\n")
wxT("]]></xsl:text></xsl:comment>\n")
wxT("</script>\n")
wxT("</html>\n")
wxT("\n")
wxT("</xsl:template>\n")
wxT("\n")
wxT("<xsl:template match=\"section\">\n")
wxT(" <xsl:if test=\"../section[@id = current()/@id]/@name != ''\">\n")
wxT(" <h2><xsl:value-of select=\"../section[@id = current()/@id]/@name\" /></h2>\n")
wxT(" </xsl:if>\n")
wxT("\n")
wxT(" <xsl:if test=\"count(../section[@id = current()/@id]/table/columns/column) > 0\">\n")
wxT(" <div class=\"tableFixHead\" >\n")
wxT(" <table>\n")
wxT(" <thead><tr>\n")
wxT(" <xsl:apply-templates select=\"../section[@id = current()/@id]/table/columns/column\">\n")
wxT(" <xsl:sort select=\"@number\" data-type=\"number\" order=\"ascending\" />\n")
wxT(" <xsl:with-param name=\"count\" select=\"count(../section[@id = current()/@id]/table/columns/column)\" />\n")
wxT(" </xsl:apply-templates>\n")
wxT(" </tr></thead><tbody>\n")
wxT(" <xsl:apply-templates select=\"../section[@id = current()/@id]/table/rows/*\" mode=\"rows\">\n")
wxT(" <xsl:sort select=\"@number\" data-type=\"number\" order=\"ascending\" />\n")
wxT(" <xsl:with-param name=\"column-meta\" select=\"../section[@id = current()/@id]/table/columns/column\" />\n")
wxT(" </xsl:apply-templates>\n")
wxT(" </tbody></table>\n")
wxT(" </div>\n")
wxT(" <br />\n")
wxT(" <xsl:if test=\"../section[@id = current()/@id]/table/info != ''\">\n")
wxT(" <p class=\"ReportTableInfo\"><xsl:value-of select=\"../section[@id = current()/@id]/table/info\" /></p>\n")
wxT(" </xsl:if>\n")
wxT(" </xsl:if>\n")
wxT("\n")
wxT(" <xsl:if test=\"../section[@id = current()/@id]/sql != ''\">\n")
wxT(" <pre class=\"ReportSQL\">\n")
wxT(" <xsl:call-template name=\"substitute\">\n")
wxT(" <xsl:with-param name=\"string\" select=\"../section[@id = current()/@id]/sql\" />\n")
wxT(" </xsl:call-template>\n")
wxT(" </pre>\n")
wxT(" </xsl:if>\n")
wxT("</xsl:template>\n")
wxT("\n")
wxT("<xsl:template match=\"column\">\n")
wxT(" <xsl:param name=\"count\" />\n")
wxT(" <th class=\"ReportTableHeaderCell\">\n")
wxT(" <xsl:attribute name=\"width\"><xsl:value-of select=\"100 div $count\" />%</xsl:attribute>\n")
wxT(" <xsl:call-template name=\"substitute\">\n")
wxT(" <xsl:with-param name=\"string\" select=\"@name\" />\n")
wxT(" </xsl:call-template>\n")
wxT(" </th>\n")
wxT("</xsl:template>\n")
wxT("\n")
wxT("<xsl:template match=\"*\" mode=\"rows\">\n")
wxT(" <xsl:param name=\"column-meta\" />\n")
wxT(" <tr>\n")
wxT(" <xsl:choose>\n")
wxT(" <xsl:when test=\"(@foot)\">\n")
wxT(" <xsl:attribute name=\"class\">ReportDetailsFootDataRow</xsl:attribute>\n")
wxT(" </xsl:when>\n")
wxT(" <xsl:when test=\"position() mod 2 != 1\">\n")
wxT(" <xsl:attribute name=\"class\">ReportDetailsOddDataRow</xsl:attribute>\n")
wxT(" </xsl:when>\n")
wxT(" <xsl:otherwise>\n")
wxT(" <xsl:attribute name=\"class\">ReportDetailsEvenDataRow</xsl:attribute>\n")
wxT(" </xsl:otherwise>\n")
wxT(" </xsl:choose>\n")
wxT(" <xsl:apply-templates select=\"$column-meta\" mode=\"cells\">\n")
wxT(" <xsl:with-param name=\"row\" select=\".\" />\n")
wxT(" </xsl:apply-templates>\n")
wxT(" </tr>\n")
wxT("</xsl:template>\n")
wxT("\n")
wxT("<xsl:template match=\"*\" mode=\"cells\">\n")
wxT(" <xsl:param name=\"row\" />\n")
wxT(" <td class=\"ReportTableValueCell\">\n")
wxT(" <xsl:choose>\n")
wxT(" <xsl:when test=\"$row/@*[name() = current()/@id]|$row/*[name() = current()/@id] != ''\">\n")
wxT(" <xsl:call-template name=\"substitute\">\n")
wxT(" <xsl:with-param name=\"string\" select=\"$row/@*[name() = current()/@id]|$row/*[name() = current()/@id]\" />\n")
wxT(" </xsl:call-template>\n")
wxT(" </xsl:when>\n")
wxT(" <xsl:otherwise>\n")
wxT(" <xsl:text> </xsl:text>\n")
wxT(" </xsl:otherwise>\n")
wxT(" </xsl:choose>\n")
wxT(" </td>\n")
wxT("</xsl:template>\n")
wxT("\n")
wxT("<xsl:template name=\"substitute\">\n")
wxT(" <xsl:param name=\"string\" />\n")
wxT(" <xsl:param name=\"from\" select=\"'&#xA;'\" />\n")
wxT(" <xsl:param name=\"to\">\n")
wxT(" <br />\n")
wxT(" </xsl:param>\n")
wxT(" <xsl:choose>\n")
wxT(" <xsl:when test=\"contains($string, $from)\">\n")
wxT(" <xsl:value-of select=\"substring-before($string, $from)\" />\n")
wxT(" <xsl:copy-of select=\"$to\" />\n")
wxT(" <xsl:call-template name=\"substitute\">\n")
wxT(" <xsl:with-param name=\"string\" select=\"substring-after($string, $from)\" />\n")
wxT(" <xsl:with-param name=\"from\" select=\"$from\" />\n")
wxT(" <xsl:with-param name=\"to\" select=\"$to\" />\n")
wxT(" </xsl:call-template>\n")
wxT(" </xsl:when>\n")
wxT(" <xsl:otherwise>\n")
wxT(" <xsl:value-of select=\"$string\" />\n")
wxT(" </xsl:otherwise>\n")
wxT(" </xsl:choose>\n")
wxT("</xsl:template>\n")
wxT("\n")
wxT("</xsl:stylesheet>\n");
return data;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// END STYLESHEET FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// START XML FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void frmReport::XmlAddHeaderValue(const wxString &name, const wxString &value)
{
header += wxT(" <") + HtmlEntities(name) + wxT(">") + HtmlEntities(value) + wxT("</") + HtmlEntities(name) + wxT(">\n");
}
int frmReport::XmlCreateSection(const wxString &name)
{
int ind = sectionName.Add(HtmlEntities(name));
sectionData.Add(wxT(""));
sectionTableHeader.Add(wxT(""));
sectionTableRows.Add(wxT(""));
sectionTableInfo.Add(wxT(""));
sectionSql.Add(wxT(""));
return ind + 1;
}
void frmReport::XmlSetSectionTableHeader(const int section, int columns, const wxChar *name, ...)
{
va_list ap;
const wxChar *p = name;
wxString data;
va_start(ap, name);
for (int x = 0; x < columns; x++)
{
data += wxT(" <column id=\"c");
data += NumToStr((long)(x + 1));
data += wxT("\" number=\"");
data += NumToStr((long)(x + 1));
data += wxT("\" name=\"");
data += HtmlEntities(p);
data += wxT("\"/>\n");
p = va_arg(ap, wxChar *);
}
va_end(ap);
sectionTableHeader[section - 1] = data;
}
void frmReport::XmlAddSectionTableRow(const int section, int number, int columns, const wxChar *value, ...)
{
va_list ap;
const wxChar *p = value;
wxString data;
va_start(ap, value);
data = wxT(" <row id=\"r");
data += NumToStr((long)number);
data += wxT("\" number=\"");
data += NumToStr((long)number);
data += wxT("\"");
for (int x = 0; x < columns; x++)
{
data += wxT(" c");
data += NumToStr((long)(x + 1));
data += wxT("=\"");
data += HtmlEntities(p);
data += wxT("\"");
p = va_arg(ap, wxChar *);
}
data += wxT(" />\n");
va_end(ap);
sectionTableRows[section - 1] += data;
}
void frmReport::XmlAddSectionTableFromListView(const int section, ctlListView *list)
{
// Get the column headers
int cols = list->GetColumnCount();
wxString data;
wxListItem itm;
//double sum = 0;
//double max = -DBL_MAX;
//double min = -DBL_MAX;
std::vector<int> isTypeNumber;
// Build the columns
for (int x = 0; x < cols; x++)
{
itm.SetMask(wxLIST_MASK_TEXT);
list->GetColumn(x, itm);
wxString label = itm.GetText();
if (label == _("Size")) isTypeNumber.push_back(2);
else if (list->IsNumberColumn(label)) isTypeNumber.push_back(1);
else
isTypeNumber.push_back(0); // string
data += wxT(" <column id=\"c");
data += NumToStr((long)(x + 1));
data += wxT("\" number=\"");
data += NumToStr((long)(x + 1));
data += wxT("\" name=\"");
data += HtmlEntities(label);
data += wxT("\" />\n");
}
sectionTableHeader[section - 1] = data;
// Build the rows
int rows = list->GetItemCount();
wxArrayString footcols;
Stats st(rows, cols, isTypeNumber);
int footrows = st.getCountMetrics();
for (int y = 0; y < rows + footrows; y++)
{
if (y >= rows) {
if (y == rows) st.calc();
// foot rows
footcols = st.GetRowForTable(y - rows);
if (footcols.Count() == 0) continue;
}
data = wxT(" <row id=\"r");
data += NumToStr((long)(y + 1));
data += wxT("\" number=\"");
data += NumToStr((long)(y + 1));
data += wxT("\"");
for (int x = 0; x < cols; x++)
{
data += wxT(" c");
data += NumToStr((long)(x + 1));
data += wxT("=\"");
if (y >= rows)
data += HtmlEntities(footcols[x]);
else {
wxString val = list->GetText(y, x);
if (val == "NaN") val = "0";
double d = 0;
int t = isTypeNumber[x];
if (t == 2) { d = ConvertSizeToMB(val); }
else
{
if (val.ToCDouble(&d)) {
}
}
st.addValue(y, x, d);
data += HtmlEntities(val);
}
data += wxT("\"");
}
if (y >= rows) data = data + " foot=\"yes\"";
data += wxT(" />\n");
sectionTableRows[section - 1] += data;
}
}
void frmReport::XmlAddSectionTableFromGrid(const int section, ctlSQLResult *grid)
{
// Get the column headers
int cols = grid->GetNumberCols();
int shift = 0;
wxString data;
wxListItem itm;
if (grid->GetRowCountSuppressed())
shift = 1;
for (int x = 1; x <= cols; x++)
{
wxString label = grid->OnGetItemText(-1, x - shift);
data += wxT(" <column id=\"c");
data += NumToStr((long)(x));
data += wxT("\" number=\"");
data += NumToStr((long)(x));
data += wxT("\" name=\"");
data += HtmlEntities(label);
data += wxT("\" />\n");
}
sectionTableHeader[section - 1] = data;
// Build the rows
int rows = grid->NumRows();
for (int y = 0; y < rows; y++)
{
data = wxT(" <row id=\"r");
data += NumToStr((long)(y + 1));
data += wxT("\" number=\"");
data += NumToStr((long)(y + 1));
data += wxT("\"");
for (int x = 1; x <= cols; x++)
{
data += wxT(" c");
data += NumToStr((long)(x));
data += wxT("=\"");
data += HtmlEntities(grid->OnGetItemText(y, x - shift));
data += wxT("\"");
}
data += wxT(" />\n");
sectionTableRows[section - 1] += data;
}
}
void frmReport::XmlSetSectionSql(int section, const wxString &sql)
{
sectionSql[section - 1] = HtmlEntities(sql);
if (!sectionSql[section - 1].IsEmpty())
chkSql->Enable();
else
chkSql->Disable();
}
void frmReport::XmlAddSectionValue(const int section, const wxString &name, const wxString &value)
{
sectionData[section - 1] += wxT(" <") + HtmlEntities(name) + wxT(">") + HtmlEntities(value) + wxT("</") + HtmlEntities(name) + wxT(">\n");
}
wxString frmReport::GetSectionTableColumns(const int section)
{
wxString data;
data = wxT(" <columns>\n");
data += sectionTableHeader[section - 1];
data += wxT(" </columns>\n");
return data;
}
wxString frmReport::GetSectionTableRows(const int section)
{
wxString data;
data = wxT(" <rows>\n");
data += sectionTableRows[section - 1];
data += wxT(" </rows>\n");
return data;
}
wxString frmReport::GetSectionTable(const int section)
{
wxString data;
data = wxT(" <table>\n");
data += GetSectionTableColumns(section);
data += GetSectionTableRows(section);
if (!sectionTableInfo[section - 1].IsEmpty())
{
data += wxT(" <info>");
data += sectionTableInfo[section - 1];
data += wxT("</info>\n");
}
data += wxT(" </table>\n");
return data;
}
wxString frmReport::GetSection(const int section)
{
wxString data;
data = wxT(" <section id=\"s");
data += NumToStr((long)section);
data += wxT("\" number=\"");
data += NumToStr((long)section);
data += wxT("\" name=\"");
data += sectionName[section - 1];
data += wxT("\">\n");
data += GetSectionTable(section);
if (chkSql->GetValue() && !sectionSql[section - 1].IsEmpty())
{
data += wxT(" <sql>");
data += sectionSql[section - 1];
data += wxT("</sql>\n");
}
data += sectionData[section - 1];
data += wxT(" </section>\n");
return data;
}
wxString frmReport::GetXmlReport(const wxString &stylesheet = wxT(""))
{
wxString data;
data = wxT("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
if (!stylesheet.IsEmpty())
{
data += wxT("<?xml-stylesheet type=\"text/xsl\" href=\"");
data += stylesheet;
data += wxT("\" ?>\n");
}
data += wxT("\n");
data += wxT("<report>\n\n");
data += wxT(" <header>\n");
data += header;
data += wxT(" </header>\n\n");
for (unsigned int x = 1; x <= sectionName.GetCount(); x++ )
{
data += GetSection(x);
data += wxT("\n");
}
data += wxT("</report>\n");
return data;
}
//
// libxml convenience macros
//
#define XML_FROM_WXSTRING(s) ((xmlChar *)(const char *)s.mb_str(wxConvUTF8))
#define WXSTRING_FROM_XML(s) wxString((char *)s, wxConvUTF8)
wxString frmReport::XslProcessReport(const wxString &xml, const wxString &xsl)
{
xmlChar *output = 0;
xmlDocPtr ssDoc = 0, xmlDoc = 0, resDoc = 0;
xsltStylesheetPtr ssPtr = 0;
int length;
wxBeginBusyCursor();
// Apply the stylesheet
xmlSubstituteEntitiesDefault (1); // Substitute entities
xmlLoadExtDtdDefaultValue = 1; // Load external entities
// Parse the stylesheet
ssDoc = xmlParseDoc(XML_FROM_WXSTRING(xsl));
if (!ssDoc)
{
wxEndBusyCursor();
wxLogError(_("Failed to parse the XML stylesheet!"));
goto cleanup;
}
ssPtr = xsltParseStylesheetDoc(ssDoc);
if (!ssPtr)
{
wxEndBusyCursor();
wxLogError(_("Failed to parse the XSL stylesheet!"));
goto cleanup;
}
// Parse the data
xmlDoc = xmlParseDoc(XML_FROM_WXSTRING(xml));
if (!xmlDoc)
{
wxEndBusyCursor();
wxLogError(_("Failed to parse the XML document!"));
goto cleanup;
}
// Apply the stylesheet
resDoc = xsltApplyStylesheet(ssPtr, xmlDoc, NULL);
if (!resDoc)
{
wxEndBusyCursor();
wxLogError(_("Failed to apply the XSL stylesheet to the XML document!"));
goto cleanup;
}
// Get the result
xsltSaveResultToString (&output, &length, resDoc, ssPtr);
if (!resDoc)
{
wxEndBusyCursor();
wxLogError(_("Failed to read the processed document!"));
goto cleanup;
}
cleanup:
// Cleanup
if (resDoc)
xmlFreeDoc(resDoc);
if (xmlDoc)
xmlFreeDoc(xmlDoc);
if (ssPtr)
xsltFreeStylesheet(ssPtr);
// This crashes - dunno why :-(
// if (ssDoc)
// xmlFreeDoc(ssDoc);
xsltCleanupGlobals();
wxEndBusyCursor();
if (output)
return WXSTRING_FROM_XML(output);
else
return wxEmptyString;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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;
}
wxString reportCompareFactory::ApplyCompareOpts(wxString sql,int metatype) {
if (sql.IsEmpty()) return "";
bool no_comment = parent->getChoiceSelect(1);
bool no_priv = parent->getChoiceSelect(0);
if (no_comment) {
int last = 0;
while (last!=-1) {
last = sql.Find("\nCOMMENT ON");
int start = last;
int end = -1;
while (last!=-1) {
last++;
if (sql.Length() == last) { last = -1; continue; }
wxChar c = sql.GetChar(last);
if (c == '\'') {
last++;
if (sql.GetChar(last) == '\'') { continue; }
if (sql.GetChar(last) == ';') { end = last; last = -1; continue; }
}
};
if (end != -1) {
sql=sql.Remove(start, end - start+1);
last = 0;
}
}
}
if (no_priv &&(metatype != PGM_ROLE)) {
int last = 0;
while (last != -1) {
last = sql.Find("\nGRANT ");
if (last==-1) last = sql.Find("\nREVOKE ");
int start = last;
int end = -1;
while (last != -1) {
last++;
if (sql.Length() == last) { last = -1; continue; }
wxChar c = sql.GetChar(last);
if (c == ';') {
end = last; last = -1;
}
};
if (end != -1) {
sql = sql.Remove(start, end - start + 1);
last = 0;
}
}
}
return sql;
}
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;
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="";
s = ApplyCompareOpts(s, obj->GetMetaType());
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()));
wxString src_serverGroupname=path.AfterFirst('/').BeforeFirst('/');
wxString trg_serverGroupname;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>/<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>/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) {
// select server
//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); // <20> /<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>N/
}
wxString p_db_replace=_("Databases")+"/"+p_db+"/";
wxString p_server_replace=src_serverGroupname+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)
{
wxString path(form->GetNodePath(server->GetId()));
trg_serverGroupname=path.AfterFirst('/').BeforeFirst('/');
// есть открытая БД
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=trg_serverGroupname+'/'+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("Not found object %s in other DB.",newpath);
wxMessageBox(msg, _("Error"), wxOK | wxICON_INFORMATION);
return 0;
}
trgobj=browser->GetObject(browser->GetSelection());
} else
{
msg="Not open connecting, compare stop.";
// msg.Printf("В установленном соединении %s, нет подходящих БД.",browser->GetItemText(lastdb->GetServer()->GetId()).BeforeFirst('(').Trim());
wxMessageBox(msg, _("Error"), wxOK | wxICON_INFORMATION);
return 0;
}
if (!parent->StartChoiceDialog()) return 0;
time_t timer=wxDateTime::GetTimeNow();
wxArrayString expandedNodes;
ArraySQL list;
MyHashSQL h_path;
wxWindowDisabler disableAll;
{
#ifndef DEBUG
wxBusyInfo waiting(wxString::Format(" Reading source DB Path = %s ,Start object = %s",
browser->GetItemText(obj->GetServer()->GetId()).c_str(), obj->GetName().c_str(),parent));
// Give the UI a chance to redraw
wxSafeYield();
wxMilliSleep(50);
wxSafeYield();
#endif
// 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(" Reading target DB Path = %s\n, Start object = %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;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
#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;
if (!wxFileExists(f)) {
f=dataDir+wxFileName::GetPathSeparator()+"textcompare_report.template";
}
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;
}
buffer.Replace('\r',wxEmptyString,true);
wxStringTokenizer lines(buffer, wxT("\n"));
Diff_EditCost=4;
Match_Threshold=0.5;
Match_Distance=1000;
bool mline=false;
while (lines.HasMoreTokens())
{
wxString tmp = lines.GetNextToken();
//if (tmp.length()>0 && tmp[tmp.length()-1]=='\r') tmp.RemoveLast();
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;}
// diff config
if (tmp.StartsWith("@Match_Distance=")) {Match_Distance=(int)StrToDouble(tmp.After('=')); continue;}
if (tmp.StartsWith("@Diff_EditCost=")) {Diff_EditCost=StrToDouble(tmp.After('=')); continue;}
if (tmp.StartsWith("@Match_Threshold=")) {Match_Threshold=(float)StrToDouble(tmp.After('=')); continue;}
head+=tmp+"\n";
}
//
{
wxBusyInfo waiting(wxString::Format(" Search for differences ...",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()+wxFileName::GetPathSeparator()+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_EditCost = 4;
Match_Threshold = 0.5;
Match_Distance = 1000;
diff_match_patch dmp(Diff_EditCost,Match_Threshold,Match_Distance);
std::list<Diff> diffs;
if (str1==str2) {
return L"";
}
diffs=dmp.diff_main(str1,str2,true);
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=\"differencei\">" + t + L"</span>";
if (aDiff.operation == Operation::DELETE) cur_l += L"<span class=\"differenced\">" + 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_l=L""; ncur_r=L"";
// if (p_ncur_r==ncur_r) { ncur_r=L""; modify=true;}
// if (p_ncur_l==ncur_l) { ncur_l=L""; modify=true;}
std::wstring t_cur_l=cur_l;
std::wstring t_cur_r=cur_r;
if (aDiff.operation==Operation::DELETE) {
t_cur_r=L"";
ncur_l=std::to_wstring(lline);
modify=true;
lline++;
} else if (aDiff.operation==Operation::INSERT) {
t_cur_l=L"";
ncur_r=std::to_wstring(rline);
modify=true;
rline++;
} else if (aDiff.operation==Operation::EQUAL) {
ncur_r=std::to_wstring(rline);
ncur_l=std::to_wstring(lline);
rline++; lline++;
}
if (modify) countdiffline++;
// if (( (ncur_r.empty()&&(!ncur_l.empty()))
// ||(ncur_l.empty()&&(!ncur_r.empty()))
// )&&(!modify)) modify=true;
// create columns
//left
tableline+=L"<tr><td class=\"diff_next\" onclick=\"c(this)\"></td>";
tableline+= modify ? L"<td class=\"has_difference\" onclick=\"d(this)\">"+ncur_l+"</td>" : L"<td class=\"diff_header\" onclick=\"d(this)\">"+ncur_l+"</td>";
tableline+=L"<td class=\"lineContent\"><pre>"+t_cur_l+"</pre></td>";
// right
tableline+=L"<td class=\"diff_next\" onclick=\"c(this)\"></td>";
tableline+= modify ? L"<td class=\"has_difference\" onclick=\"d(this)\">"+ncur_r+"</td>" : L"<td class=\"diff_header\" onclick=\"d(this)\">"+ncur_r+"</td>";
tableline+=L"<td class=\"lineContent\"><pre>"+t_cur_r+"</pre></td>";
tableline+=L"</tr>";
if (ncur_r.length()>0) p_ncur_r=ncur_r;
if (ncur_l.length()>0) p_ncur_l=ncur_l;
if (aDiff.operation==Operation::DELETE) {
cur_l=L"";
} else if (aDiff.operation==Operation::INSERT) {
cur_r=L"";
} else if (aDiff.operation==Operation::EQUAL) {
cur_r=L"";cur_l=L"";
}
//
modify=false;
oneline=false;
}
} // цикл по строкам внутри одного Diff
++it;
}
#ifdef _DEBUG
wxFileName fn("cmp_debug.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(" Table\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());
t1.erase(std::remove(t1.begin(), t1.end(), '\r'), t1.end());
t2.erase(std::remove(t2.begin(), t2.end(), '\r'), t2.end());
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
///////////////////////////////////////////////////////
wxWindow *reportBaseFactory::StartDialog(frmMain *form, pgObject *obj)
{
parent = form;
wxBeginBusyCursor();
frmReport *report = new frmReport(GetFrmMain());
// Generate the report header
wxDateTime now = wxDateTime::Now();
report->XmlAddHeaderValue(wxT("generated"), now.Format(wxT("%c")));
if (obj->GetServer())
report->XmlAddHeaderValue(wxT("server"), obj->GetServer()->GetFullIdentifier());
if (obj->GetDatabase())
report->XmlAddHeaderValue(wxT("database"), obj->GetDatabase()->GetName());
if (obj->GetSchema())
{
if (obj->GetSchema()->GetMetaType() == PGM_CATALOG)
report->XmlAddHeaderValue(wxT("catalog"), obj->GetSchema()->GetDisplayName());
else
report->XmlAddHeaderValue(wxT("schema"), obj->GetSchema()->GetName());
}
if (obj->GetJob())
report->XmlAddHeaderValue(wxT("job"), obj->GetJob()->GetName());
if (obj->GetTable())
report->XmlAddHeaderValue(wxT("table"), obj->GetTable()->GetName());
GenerateReport(report, obj);
wxEndBusyCursor();
report->ShowModal();
return 0;
}
///////////////////////////////////////////////////////
// Properties report
///////////////////////////////////////////////////////
reportObjectPropertiesFactory::reportObjectPropertiesFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar)
: reportBaseFactory(list)
{
mnu->Append(id, _("&Properties Report"), _("Generate a Properties report for this object."));
}
bool reportObjectPropertiesFactory::CheckEnable(pgObject *obj)
{
if (obj)
{
if (obj->IsCollection())
return false;
else
return true;
}
return false;
}
void reportObjectPropertiesFactory::GenerateReport(frmReport *report, pgObject *object)
{
report->SetReportTitle(object->GetTranslatedMessage(PROPERTIESREPORT));
int section = report->XmlCreateSection(object->GetTranslatedMessage(PROPERTIES));
ctlListView *list = GetFrmMain()->GetProperties();
object->ShowProperties();
report->XmlAddSectionTableFromListView(section, list);
report->XmlSetSectionSql(section, object->GetSql(NULL));
}
///////////////////////////////////////////////////////
// DDL report
///////////////////////////////////////////////////////
reportObjectDdlFactory::reportObjectDdlFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar)
: reportBaseFactory(list)
{
mnu->Append(id, _("&DDL Report"), _("Generate a DDL report for this object."));
}
bool reportObjectDdlFactory::CheckEnable(pgObject *obj)
{
if (obj)
{
if (obj->GetMetaType() == PGM_SERVER || obj->GetMetaType() == PGM_CATALOGOBJECT || obj->IsCollection())
return false;
else
return true;
}
return false;
}
void reportObjectDdlFactory::GenerateReport(frmReport *report, pgObject *object)
{
report->SetReportTitle(object->GetTranslatedMessage(DDLREPORT));
int section = report->XmlCreateSection(object->GetTranslatedMessage(DDL));
report->XmlSetSectionSql(section, object->GetSql(NULL));
}
///////////////////////////////////////////////////////
// Data dictionary report
///////////////////////////////////////////////////////
reportObjectDataDictionaryFactory::reportObjectDataDictionaryFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar)
: reportBaseFactory(list)
{
mnu->Append(id, _("&Data Dictionary Report"), _("Generate a Data Dictionary report for this object."));
}
bool reportObjectDataDictionaryFactory::CheckEnable(pgObject *obj)
{
if (obj)
{
if ((obj->GetMetaType() == PGM_TABLE || obj->GetMetaType() == GP_PARTITION) && !obj->IsCollection())
return true;
else
return false;
}
return false;
}
void reportObjectDataDictionaryFactory::GenerateReport(frmReport *report, pgObject *object)
{
pgTable *table = (pgTable *)object;
report->SetReportTitle(object->GetTranslatedMessage(DATADICTIONNARYREPORT));
// Columns
int section = report->XmlCreateSection(_("Columns"));
report->XmlSetSectionTableHeader(section, 6, (const wxChar *) _("Name"), (const wxChar *) _("Data type"), (const wxChar *) _("Not Null?"), (const wxChar *) _("Primary key?"), (const wxChar *) _("Default"), (const wxChar *) _("Comment"));
ctlTree *browser = GetFrmMain()->GetBrowser();
pgCollection *columns = table->GetColumnCollection(browser);
treeObjectIterator colIt(browser, columns);
pgColumn *column;
bool haveInherit = false;
wxString colName;
while ((column = (pgColumn *)colIt.GetNextObject()) != 0)
{
column->ShowTreeDetail(browser);
if (column->GetColNumber() > 0)
{
colName = column->GetName();
if (column->GetInheritedCount() > 0)
{
colName += _("*");
haveInherit = true;
}
report->XmlAddSectionTableRow(section,
column->GetColNumber(),
6,
(const wxChar *) colName,
(const wxChar *) column->GetVarTypename(),
(const wxChar *) BoolToYesNo(column->GetNotNull()),
(const wxChar *) BoolToYesNo(column->GetIsPK()),
(const wxChar *) column->GetDefault(),
(const wxChar *) column->GetComment());
}
}
if (haveInherit)
{
wxString info;
info.Printf(_("* Inherited columns from %s."), table->GetInheritedTables().c_str());
report->XmlSetSectionTableInfo(section, info);
}
// Constraints
pgCollection *constraints = table->GetConstraintCollection(browser);
treeObjectIterator conIt(browser, constraints);
pgObject *constraint;
long x = 1;
wxString definition, type;
while ((constraint = (pgObject *)conIt.GetNextObject()) != 0)
{
if (x == 1)
{
section = report->XmlCreateSection(_("Constraints"));
report->XmlSetSectionTableHeader(section, 4, (const wxChar *) _("Name"), (const wxChar *) _("Type"), (const wxChar *) _("Definition"), (const wxChar *) _("Comment"));
}
constraint->ShowTreeDetail(browser);
switch (constraint->GetMetaType())
{
case PGM_PRIMARYKEY:
type = _("Primary key");
definition = ((pgIndexConstraint *)constraint)->GetDefinition();
break;
case PGM_UNIQUE:
type = _("Unique");
definition = ((pgIndexConstraint *)constraint)->GetDefinition();
break;
case PGM_FOREIGNKEY:
type = _("Foreign key");
definition = ((pgForeignKey *)constraint)->GetDefinition(PGM_FOREIGNKEY);
break;
case PGM_EXCLUDE:
type = _("Exclude");
definition = ((pgIndexConstraint *)constraint)->GetDefinition();
break;
case PGM_CHECK:
type = _("Check");
definition = wxT("(") + ((pgCheck *)constraint)->GetDefinition() + wxT(")");
break;
}
report->XmlAddSectionTableRow(section,
x,
4,
(const wxChar *) constraint->GetName(),
(const wxChar *) type,
(const wxChar *) definition,
(const wxChar *) constraint->GetComment());
x++;
}
}
///////////////////////////////////////////////////////
// Statistics report
///////////////////////////////////////////////////////
reportObjectStatisticsFactory::reportObjectStatisticsFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar)
: reportBaseFactory(list)
{
mnu->Append(id, _("&Statistics Report"), _("Generate a Statistics report for this object."));
}
bool reportObjectStatisticsFactory::CheckEnable(pgObject *obj)
{
if (obj)
{
if (!obj->HasStats())
{
if (obj->IsCollection())
{
pgaFactory *f = obj->GetFactory();
if (f)
{
if (f->GetMetaType() == PGM_TABLE ||
f->GetMetaType() == GP_PARTITION ||
f->GetMetaType() == PGM_TABLESPACE ||
f->GetMetaType() == PGM_DATABASE)
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
else
{
return true;
}
}
return false;
}
void reportObjectStatisticsFactory::GenerateReport(frmReport *report, pgObject *object)
{
report->SetReportTitle(object->GetTranslatedMessage(STATISTICSREPORT));
int section = report->XmlCreateSection(object->GetTranslatedMessage(OBJSTATISTICS));
ctlListView *list = GetFrmMain()->GetStatistics();
object->ShowStatistics(GetFrmMain(), list);
report->XmlAddSectionTableFromListView(section, list);
}
///////////////////////////////////////////////////////
// Dependencies report
///////////////////////////////////////////////////////
reportObjectDependenciesFactory::reportObjectDependenciesFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar)
: reportBaseFactory(list)
{
mnu->Append(id, _("&Dependencies Report"), _("Generate a Dependencies report for this object."));
}
bool reportObjectDependenciesFactory::CheckEnable(pgObject *obj)
{
if (obj)
{
if (!obj->HasDepends())
return false;
else
return true;
}
return false;
}
void reportObjectDependenciesFactory::GenerateReport(frmReport *report, pgObject *object)
{
report->SetReportTitle(object->GetTranslatedMessage(DEPENDENCIESREPORT));
int section = report->XmlCreateSection(object->GetTranslatedMessage(DEPENDENCIES));
ctlListView *list = GetFrmMain()->GetDependencies();
object->ShowDependencies(parent, list);
report->XmlAddSectionTableFromListView(section, list);
}
///////////////////////////////////////////////////////
// Dependents report
///////////////////////////////////////////////////////
reportObjectDependentsFactory::reportObjectDependentsFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar)
: reportBaseFactory(list)
{
mnu->Append(id, _("&Dependents Report"), _("Generate a Dependents report for this object."));
}
bool reportObjectDependentsFactory::CheckEnable(pgObject *obj)
{
if (obj)
{
if (!obj->HasReferences())
return false;
else
return true;
}
return false;
}
void reportObjectDependentsFactory::GenerateReport(frmReport *report, pgObject *object)
{
report->SetReportTitle(object->GetTranslatedMessage(DEPENDENTSREPORT));
int section = report->XmlCreateSection(object->GetTranslatedMessage(DEPENDENTS));
ctlListView *list = GetFrmMain()->GetReferencedBy();
object->ShowDependents(parent, list);
report->XmlAddSectionTableFromListView(section, list);
}
///////////////////////////////////////////////////////
// Object list report
///////////////////////////////////////////////////////
reportObjectListFactory::reportObjectListFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar)
: reportBaseFactory(list)
{
mnu->Append(id, _("&Object List Report"), _("Generate an Object List report for this collection."));
}
bool reportObjectListFactory::CheckEnable(pgObject *obj)
{
if (obj)
{
if (!obj->IsCollection())
{
return false;
}
else
{
pgaFactory *f = obj->GetFactory();
if (obj)
{
if (f->GetMetaType() == PGM_SERVER)
return false;
}
else
return false;
return true;
}
}
return false;
}
void reportObjectListFactory::GenerateReport(frmReport *report, pgObject *object)
{
report->SetReportTitle(object->GetTranslatedMessage(OBJECTSLISTREPORT));
int section = report->XmlCreateSection(object->GetFullIdentifier());
ctlListView *list = GetFrmMain()->GetProperties();
object->ShowProperties();
report->XmlAddSectionTableFromListView(section, list);
}