3
0
mirror of https://github.com/Qortal/Brooklyn.git synced 2025-02-12 10:15:54 +00:00

251 lines
8.0 KiB
C++
Raw Normal View History

2022-03-05 22:41:29 +05:00
/*
SPDX-FileCopyrightText: 2007 Jeff Cooper <weirdsox11@gmail.com>
SPDX-FileCopyrightText: 2007 Thomas Georgiou <TAGeorgiou@gmail.com>
SPDX-License-Identifier: LGPL-2.0-only
*/
#include "dictengine.h"
#include <iostream>
#include <KLocalizedString>
#include <QDebug>
#include <QRegularExpression>
#include <QTcpSocket>
#include <QUrl>
#include <Plasma/DataContainer>
DictEngine::DictEngine(QObject *parent, const QVariantList &args)
: Plasma::DataEngine(parent, args)
, m_tcpSocket(nullptr)
{
Q_UNUSED(args)
m_serverName = QLatin1String("dict.org"); // In case we need to switch it later
m_dictName = QLatin1String("wn"); // Default, good dictionary
}
DictEngine::~DictEngine()
{
}
void DictEngine::setDict(const QString &dict)
{
m_dictName = dict;
}
void DictEngine::setServer(const QString &server)
{
m_serverName = server;
}
static QString wnToHtml(const QString &word, QByteArray &text)
{
QList<QByteArray> splitText = text.split('\n');
QString def;
def += QLatin1String("<dl>\n");
static QRegularExpression linkRx(QStringLiteral("{(.*?)}"));
bool isFirst = true;
while (!splitText.empty()) {
// 150 n definitions retrieved - definitions follow
// 151 word database name - text follows
// 250 ok (optional timing information here)
// 552 No match
QString currentLine = splitText.takeFirst();
if (currentLine.startsWith(QLatin1String("151"))) {
isFirst = true;
continue;
}
if (currentLine.startsWith('.')) {
def += QLatin1String("</dd>");
continue;
}
if (currentLine.startsWith("552")) {
return i18n("No match found for %1", word);
}
if (!(currentLine.startsWith(QLatin1String("150")) || currentLine.startsWith(QLatin1String("151")) || currentLine.startsWith(QLatin1String("250")))) {
// Handle links
int offset = 0;
QRegularExpressionMatchIterator it = linkRx.globalMatch(currentLine);
while (it.hasNext()) {
QRegularExpressionMatch match = it.next();
QUrl url;
url.setScheme("dict");
url.setPath(match.captured(1));
const QString linkText = QStringLiteral("<a href=\"%1\">%2</a>").arg(url.toString(), match.captured(1));
currentLine.replace(match.capturedStart(0) + offset, match.capturedLength(0), linkText);
offset += linkText.length() - match.capturedLength(0);
}
if (isFirst) {
def += "<dt><b>" + currentLine + "</b></dt>\n<dd>";
isFirst = false;
continue;
} else {
static QRegularExpression newLineRx(QStringLiteral("([1-9]{1,2}:)"));
if (currentLine.contains(newLineRx)) {
def += QLatin1String("\n<br>\n");
}
static QRegularExpression makeMeBoldRx(QStringLiteral("^([\\s\\S]*[1-9]{1,2}:)"));
currentLine.replace(makeMeBoldRx, QLatin1String("<b>\\1</b>"));
def += currentLine;
continue;
}
}
}
def += QLatin1String("</dl>");
return def;
}
void DictEngine::getDefinition()
{
m_tcpSocket->readAll();
QByteArray ret;
const QByteArray command = QByteArray("DEFINE ") + m_dictName.toLatin1() + " \"" + m_currentWord.toUtf8() + "\"\n";
// qDebug() << command;
m_tcpSocket->write(command);
m_tcpSocket->flush();
while (!ret.contains("250") && !ret.contains("552") && !ret.contains("550")) {
m_tcpSocket->waitForReadyRead();
ret += m_tcpSocket->readAll();
}
m_tcpSocket->disconnectFromHost();
const QString html = wnToHtml(m_currentWord, ret);
// setData(m_currentQuery, m_dictName, html);
setData(m_currentQuery, QStringLiteral("text"), html);
}
void DictEngine::getDicts()
{
m_tcpSocket->readAll();
QByteArray ret;
m_tcpSocket->write(QByteArray("SHOW DB\n"));
m_tcpSocket->flush();
m_tcpSocket->waitForReadyRead();
while (!ret.contains("250")) {
m_tcpSocket->waitForReadyRead();
ret += m_tcpSocket->readAll();
}
QVariantMap *availableDicts = new QVariantMap;
const QList<QByteArray> retLines = ret.split('\n');
for (const QByteArray &curr : retLines) {
if (curr.startsWith("554")) {
// TODO: What happens if no DB available?
// TODO: Eventually there will be functionality to change the server...
break;
}
// ignore status code and empty lines
if (curr.startsWith("250") || curr.startsWith("110") || curr.isEmpty()) {
continue;
}
if (!curr.startsWith('-') && !curr.startsWith('.')) {
const QString line = QString::fromUtf8(curr).trimmed();
const QString id = line.section(' ', 0, 0);
QString description = line.section(' ', 1);
if (description.startsWith('"') && description.endsWith('"')) {
description.remove(0, 1);
description.chop(1);
}
setData(QStringLiteral("list-dictionaries"), id, description); // this is additive
availableDicts->insert(id, description);
}
}
m_availableDictsCache.insert(m_serverName, availableDicts);
m_tcpSocket->disconnectFromHost();
}
void DictEngine::socketClosed()
{
if (m_tcpSocket) {
m_tcpSocket->deleteLater();
}
m_tcpSocket = nullptr;
}
bool DictEngine::sourceRequestEvent(const QString &query)
{
// FIXME: this is COMPLETELY broken .. it can only look up one query at a time!
// a DataContainer subclass that does the look up should probably be made
if (m_tcpSocket) {
m_tcpSocket->abort(); // stop if lookup is in progress and new query is requested
m_tcpSocket->deleteLater();
m_tcpSocket = nullptr;
}
QStringList queryParts = query.split(':', Qt::SkipEmptyParts);
if (queryParts.isEmpty()) {
return false;
}
m_currentWord = queryParts.last();
m_currentQuery = query;
// asked for a dictionary?
if (queryParts.count() > 1) {
setDict(queryParts[queryParts.count() - 2]);
// default to wordnet
} else {
setDict(QStringLiteral("wn"));
}
// asked for a server?
if (queryParts.count() > 2) {
setServer(queryParts[queryParts.count() - 3]);
// default to wordnet
} else {
setServer(QStringLiteral("dict.org"));
}
if (m_currentWord.simplified().isEmpty()) {
setData(m_currentQuery, m_dictName, QString());
} else {
if (m_currentWord == QLatin1String("list-dictionaries")) {
// Use cache if available
QVariantMap *dicts = m_availableDictsCache.object(m_serverName);
if (dicts) {
for (auto it = dicts->constBegin(); it != dicts->constEnd(); ++it) {
setData(m_currentQuery, it.key(), it.value());
}
return true;
}
}
// We need to do this in order to create the DataContainer immediately in DataEngine
// so it can connect to updates. Not sure why DataEnginePrivate::requestSource
// doesn't create the DataContainer when sourceRequestEvent returns true, by doing
// source(sourceName) instead of source(sourceName, false), but well, I'm too scared to change that.
setData(m_currentQuery, QVariant());
m_tcpSocket = new QTcpSocket(this);
connect(m_tcpSocket, &QTcpSocket::disconnected, this, &DictEngine::socketClosed);
if (m_currentWord == QLatin1String("list-dictionaries")) {
connect(m_tcpSocket, &QTcpSocket::readyRead, this, &DictEngine::getDicts);
} else {
connect(m_tcpSocket, &QTcpSocket::readyRead, this, &DictEngine::getDefinition);
}
m_tcpSocket->connectToHost(m_serverName, 2628);
}
return true;
}
K_PLUGIN_CLASS_WITH_JSON(DictEngine, "plasma-dataengine-dict.json")
#include "dictengine.moc"