pgLab/pglablib/util.cpp

403 lines
9.8 KiB
C++
Raw Normal View History

#include "util.h"
#include "CsvWriter.h"
#include "SqlLexer.h"
#include <QApplication>
#include <QTextStream>
#include <QClipboard>
2017-02-05 08:23:06 +01:00
#include <sstream>
#include <stdexcept>
// Supported range from microseconds to seconds
// min:sec to hours::min::sec
QString msfloatToHumanReadableString(float ms)
{
QString unit;
float val;
int deci = 2;
if (ms < 1.0f) {
val = ms * 1000.f;
//result = QString::asprintf("%0.3f", ms * 1000.0f);
unit = u8"μs";
}
else if (ms >= 1000.0) {
val = ms / 1000.0f;
unit = "s";
if (val >= 60.0) {
int secs = val;
int min = secs / 60.0;
secs -= min * 60;
if (min >= 60) {
int hour = min / 60;
min -= hour * 60;
return QString::asprintf("%d:%02d:%02d", hour, min, secs);
}
else {
return QString::asprintf("%02d:%02d", min, secs);
}
}
}
else {
val = ms;
unit = "ms";
}
// if (val >= 1000.f) {
// deci = 0;
// }
// else
if (val >= 100.f) {
deci = 0;
}
else if (val >= 10.f) {
deci = 1;
}
QString result = QString::asprintf("%0.*f", deci, val);
return result + unit;
}
2017-02-05 13:35:41 +01:00
void exportTable(const QTableView *view, QTextStream &out)
{
auto model = view->model();
if (model) {
CsvWriter csv(&out);
csv.setSeperator('\t');
csv.setQuote('"');
auto cols = model->columnCount();
auto rows = model->rowCount();
for (auto row = 0; row < rows; ++row) {
2017-02-05 13:35:41 +01:00
for (int col = 0; col < cols; ++col) {
auto idx = model->index(row, col);
auto display_text = idx.data(Qt::DisplayRole).toString();
csv.writeField(display_text);
}
csv.nextRow();
}
out.flush();
}
}
void copySelectionToClipboard(const QTableView *view)
{
//QAbstractItemModel * model = resultModel; //view->model();
QItemSelectionModel * selection = view->selectionModel();
QModelIndexList selectedIndexes = selection->selectedIndexes();
if (selectedIndexes.count() > 0) {
QString clipboard_string;
QTextStream out(&clipboard_string, QIODevice::WriteOnly);
CsvWriter csv(&out);
csv.setSeperator('\t');
csv.setQuote('"');
int previous_row = selectedIndexes[0].row();
// for (int i = 0; i < selectedIndexes.count(); ++i) {
// QModelIndex current = selectedIndexes[i];
for (auto current : selectedIndexes) {
if (current.row() > previous_row) {
csv.nextRow();
previous_row = current.row();
}
QString display_text = current.data(Qt::DisplayRole).toString();
csv.writeField(display_text);
}
out.flush();
QApplication::clipboard()->setText(clipboard_string);
}
}
2017-02-05 08:23:06 +01:00
QString ConvertToMultiLineCString(const QString &in_)
2017-02-05 08:23:06 +01:00
{
// We need to atleast escape " and \ and also any multi byte utf8 char
2017-02-05 08:23:06 +01:00
// remove empty lines at start
int last_nl_idx = 0;
for (int idx = 0; idx < in_.length(); ++idx) {
QChar c = in_[idx];
if (c == '\n') last_nl_idx = idx+1;
if (!c.isSpace()) {
break;
2017-02-05 08:23:06 +01:00
}
}
QString in = in_.right(in_.length() - last_nl_idx);
int idx;
for (idx = in.length() - 1; idx >= 0 && in[idx].isSpace(); --idx) ;
++idx;
in.truncate(idx);
SqlLexer lexer(in, LexerState::Null, true);
QString out;
QString line = "\"";
QString comment;
while (true) {
SqlToken token = lexer.nextBasicToken();
if (token.ok) {
if (token.tokenType == BasicTokenType::Comment) {
// save comment is seperate variable
comment = "//" + token.out.rightRef(token.out.length()-2);
// Trim whitespace on right
int idx;
for (idx = comment.length() - 1; idx >= 0 && comment[idx].isSpace(); --idx) ;
++idx;
comment.truncate(idx);
}
else if (token.tokenType == BasicTokenType::End || token.tokenType == BasicTokenType::NewLine) {
// trim right
{
int idx;
for (idx = line.length() - 1; idx >= 0 && line[idx].isSpace(); --idx) ;
++idx;
if (!comment.isEmpty()) {
// put the whitespace in front of the comment so it will be outside the contents of the string literal but alignment of comments is preserved
comment = line.rightRef(line.length() - (idx)) + comment;
}
line.truncate(idx);
}
out += line;
if (token.tokenType == BasicTokenType::End) {
out += "\"";
out += comment;
break;
}
else {
out += "\\n\"";
out += comment;
out += "\n";
line = "\"";
}
comment.clear();
}
else {
line += token.out;
}
2017-02-05 08:23:06 +01:00
}
else {
// error during lexical analysis, need to recover
throw std::runtime_error("Unrecognized input");
2017-02-05 08:23:06 +01:00
}
}
return out;
}
QString ConvertToMultiLineRawCppString(const QString &in)
{
const QString delim = "__SQL__";
QString out;
out.append("R\"" + delim + "(\n");
out.append(in.trimmed());
out.append("\n)" + delim + "\"");
return out;
}
QString ConvertLangToSqlString(const QString &in)
{
// Assume mostly C++ for now but allow some other things like
// - single quotes (php)
// - concattenation operators . (php) and + (java)
// Allow cpp prefixes L u8 u U
// Parser flow, we start in whitespace state and search for prefix|opening quote
// parse string and process escapes
// if escape is \r strip it if \n go to new line
// until we reach matching end quote
// skip whitespace and + or .
QString output;
enum {
WHITESPACE,
PREFIX,
IN_STRING,
END,
ERROR
} state = WHITESPACE;
int index = 0;
QChar quote = '\0';
while (state != ERROR && state != END && index < in.length()) {
if (state == WHITESPACE) {
// skip all whitespace untill we encounter something else
// we also skip concatenation operators. Note this code is not trying to validate
// for correct syntax so it will quite happily accept many incorrect constructs
// that doesn't matter however as we are just trying to strip everything which is not SQL.
while (index < in.length() && (in[index].isSpace() || in[index] == '+' || in[index] == '.')) ++index;
if (index == in.length()) {
state = END;
break;
}
// Assume quotes can vary
if (in[index] == '\'' || in[index] == '\"') {
quote = in[index];
++index;
state = IN_STRING;
}
else {
state = PREFIX;
}
}
else if (state == PREFIX) {
auto c = in[index];
if (c == 'L' || c == 'U') {
// C++ prefix expect C++ double quote
if (in.length() > index+ 1 && in[index+1] == '"') {
index += 2;
state = IN_STRING;
}
else {
state = ERROR;
break;
}
}
if (c == 'u') {
// C++ prefix expect C++ double quote
if (in.length() > index+ 2 && in[index+1] == '8' && in[index+2] == '"') {
index += 3;
state = IN_STRING;
}
else if (in.length() > index+ 1 && in[index+1] == '"') {
index += 2;
state = IN_STRING;
}
else {
state = ERROR;
break;
}
}
else {
state = ERROR;
break;
}
}
else if (state == IN_STRING) {
// scan contents of string and process any escapes encountered
bool escape = false;
while (state != ERROR && index < in.length()) {
QChar c = in[index];
if (escape) {
if (c == 'a') output += '\x07';
else if (c == 'a') output += '\x07';
else if (c == 'b') output += '\x08';
else if (c == 'f') output += '\x0c';
else if (c == 'n') output += '\n';
else if (c == 'r') ;
else if (c == 'v') ;
else if (c >= '0' && c <= '7') {
// process octal escape
if (in.length() > index + 2) {
char buf[4];
buf[0] = c.toLatin1();
buf[1] = in[++index].toLatin1();
buf[2] = in[++index].toLatin1();
buf[3] = 0;
long int v = strtol(buf, nullptr, 8);
if (v < 0x80) {
output += static_cast<QChar>(static_cast<char>(v));
}
else {
state = ERROR;
break;
}
}
else {
state = ERROR;
break;
}
}
else if (c == 'x') {
// hex byte
if (in.length() > index + 2) {
char buf[3];
buf[0] = in[++index].toLatin1();
buf[1] = in[++index].toLatin1();
buf[2] = 0;
long int v = strtol(buf, nullptr, 16);
output += static_cast<QChar>(static_cast<char>(v));
}
else {
state = ERROR;
break;
}
}
else if (c == 'u') {
// 4 digit hax unicode codepoint
// hex byte
if (in.length() > index + 4) {
char buf[5];
buf[0] = in[++index].toLatin1();
buf[1] = in[++index].toLatin1();
buf[2] = in[++index].toLatin1();
buf[3] = in[++index].toLatin1();
buf[4] = 0;
long int v = strtol(buf, nullptr, 16);
output += static_cast<QChar>(static_cast<ushort>(v));
}
else {
state = ERROR;
break;
}
}
else if (c == 'U') {
// 8 digit hax unicode codepoint
if (in.length() > index + 8) {
char buf[9];
buf[0] = in[++index].toLatin1();
buf[1] = in[++index].toLatin1();
buf[2] = in[++index].toLatin1();
buf[3] = in[++index].toLatin1();
buf[4] = in[++index].toLatin1();
buf[5] = in[++index].toLatin1();
buf[6] = in[++index].toLatin1();
buf[7] = in[++index].toLatin1();
buf[8] = 0;
uint v = static_cast<uint>(strtol(buf, nullptr, 16));
if (QChar::requiresSurrogates(v)) {
output += QChar(QChar::highSurrogate(v));
output += QChar(QChar::lowSurrogate(v));
}
}
else {
state = ERROR;
break;
}
}
else {
output += c;
}
escape = false;
}
else {
if (c == quote) {
state = WHITESPACE;
++index;
break;
}
// Is there any language where string literals do not have to be terminated before the end of the line
// Not considering string literals that explicitly allow for multiline strings as these are often raw strings
// and can be copy pasted normally.
else if (c == '\n') {
state = WHITESPACE;
++index;
break;
}
else if (c == '\\') {
escape = true;
}
else {
output += c;
}
}
++index;
}
}
}
return output;
}