1 /**
2 * UGENE - Integrated Bioinformatics Tools.
3 * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4 * http://ugene.net
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22 #include "ExternalToolRunTask.h"
23
24 #include <QDir>
25 #include <QRegularExpression>
26
27 #include <U2Core/AnnotationTableObject.h>
28 #include <U2Core/AppContext.h>
29 #include <U2Core/AppSettings.h>
30 #include <U2Core/CmdlineTaskRunner.h>
31 #include <U2Core/ExternalToolRegistry.h>
32 #include <U2Core/GUrlUtils.h>
33 #include <U2Core/Log.h>
34 #include <U2Core/ScriptingToolRegistry.h>
35 #include <U2Core/U2SafePoints.h>
36 #include <U2Core/UserApplicationsSettings.h>
37
38 #ifdef Q_OS_WIN
39 # include <windows.h>
40 #endif
41
42 #ifdef Q_OS_UNIX
43 # include <signal.h>
44 # include <unistd.h>
45 #endif
46
47 namespace U2 {
48
49 #define WIN_LAUNCH_CMD_COMMAND "cmd /C "
50 #define START_WAIT_MSEC 3000
51
ExternalToolRunTask(const QString & _toolId,const QStringList & _arguments,ExternalToolLogParser * _logParser,const QString & _workingDirectory,const QStringList & _additionalPaths,bool parseOutputFile)52 ExternalToolRunTask::ExternalToolRunTask(const QString &_toolId, const QStringList &_arguments, ExternalToolLogParser *_logParser, const QString &_workingDirectory, const QStringList &_additionalPaths, bool parseOutputFile)
53 : Task(AppContext::getExternalToolRegistry()->getToolNameById(_toolId) + tr(" tool"), TaskFlag_None),
54 arguments(_arguments),
55 logParser(_logParser),
56 toolId(_toolId),
57 workingDirectory(_workingDirectory),
58 additionalPaths(_additionalPaths),
59 externalToolProcess(nullptr),
60 helper(nullptr),
61 listener(nullptr),
62 parseOutputFile(parseOutputFile) {
63 CHECK_EXT(AppContext::getExternalToolRegistry()->getById(toolId) != nullptr, stateInfo.setError(tr("External tool is absent")), );
64
65 toolName = AppContext::getExternalToolRegistry()->getToolNameById(toolId);
66 coreLog.trace("Creating run task for: " + toolName);
67 if (logParser != nullptr) {
68 logParser->setParent(this);
69 }
70 }
71
~ExternalToolRunTask()72 ExternalToolRunTask::~ExternalToolRunTask() {
73 delete externalToolProcess;
74 }
75
run()76 void ExternalToolRunTask::run() {
77 if (hasError() || isCanceled()) {
78 return;
79 }
80
81 ProcessRun pRun = ExternalToolSupportUtils::prepareProcess(toolId, arguments, workingDirectory, additionalPaths, stateInfo, listener);
82 CHECK_OP(stateInfo, );
83 externalToolProcess = pRun.process;
84
85 if (!inputFile.isEmpty()) {
86 externalToolProcess->setStandardInputFile(inputFile);
87 }
88 if (!outputFile.isEmpty()) {
89 externalToolProcess->setStandardOutputFile(outputFile);
90 }
91 if (!additionalEnvVariables.isEmpty()) {
92 QProcessEnvironment processEnvironment = externalToolProcess->processEnvironment();
93 foreach (const QString &envVarName, additionalEnvVariables.keys()) {
94 processEnvironment.insert(envVarName, additionalEnvVariables.value(envVarName));
95 }
96 externalToolProcess->setProcessEnvironment(processEnvironment);
97 }
98
99 helper.reset(new ExternalToolRunTaskHelper(this));
100 if (listener != nullptr) {
101 helper->addOutputListener(listener);
102 }
103
104 externalToolProcess->start(pRun.program, pRun.arguments);
105 bool started = externalToolProcess->waitForStarted(START_WAIT_MSEC);
106
107 if (!started) {
108 ExternalTool *tool = AppContext::getExternalToolRegistry()->getById(toolId);
109 if (tool->isValid()) {
110 stateInfo.setError(tr("Can not run %1 tool.").arg(toolName));
111 } else {
112 stateInfo.setError(tr("Can not run %1 tool. May be tool path '%2' not valid?")
113 .arg(toolName)
114 .arg(AppContext::getExternalToolRegistry()->getById(toolId)->getPath()));
115 }
116 return;
117 }
118 while (!externalToolProcess->waitForFinished(1000)) {
119 if (isCanceled()) {
120 killProcess(externalToolProcess);
121 algoLog.details(tr("Tool %1 is cancelled").arg(toolName));
122 return;
123 }
124 }
125
126 {
127 QProcess::ExitStatus status = externalToolProcess->exitStatus();
128 int exitCode = externalToolProcess->exitCode();
129 if (status == QProcess::CrashExit && !hasError()) {
130 QString error = parseStandartOutputFile();
131 if (error.isEmpty()) {
132 QString intendedError = tr("%1 tool exited with the following error: %2 (Code: %3)")
133 .arg(toolName)
134 .arg(externalToolProcess->errorString())
135 .arg(externalToolProcess->exitCode());
136 parseError(intendedError);
137 error = logParser->getLastError();
138 }
139
140 setError(error);
141 } else if (status == QProcess::NormalExit && exitCode != EXIT_SUCCESS && !hasError()) {
142 QString error = parseStandartOutputFile();
143 setError(error.isEmpty() ? tr("%1 tool exited with code %2").arg(toolName).arg(exitCode) : error);
144 } else if (status == QProcess::NormalExit && exitCode == EXIT_SUCCESS && !hasError()) {
145 algoLog.details(tr("Tool %1 finished successfully").arg(toolName));
146 }
147 }
148 }
149
killProcess(QProcess * process)150 void ExternalToolRunTask::killProcess(QProcess *process) {
151 CmdlineTaskRunner::killProcessTree(process);
152 }
153
getChildPidsRecursive(long parentPid)154 QList<long> ExternalToolRunTask::getChildPidsRecursive(long parentPid) {
155 return CmdlineTaskRunner::getChildrenProcesses(parentPid);
156 }
157
addOutputListener(ExternalToolListener * outputListener)158 void ExternalToolRunTask::addOutputListener(ExternalToolListener *outputListener) {
159 if (helper) {
160 helper->addOutputListener(outputListener);
161 }
162 listener = outputListener;
163 }
164
parseStandartOutputFile() const165 QString ExternalToolRunTask::parseStandartOutputFile() const {
166 CHECK(parseOutputFile, QString());
167
168 QFile f(outputFile);
169 CHECK(f.open(QIODevice::ReadOnly), QString());
170
171 QString output;
172 for (QByteArray line = f.readLine(); line.length() > 0; line = f.readLine()) {
173 output += line;
174 }
175 f.close();
176 logParser->parseOutput(output);
177
178 return logParser->getLastError();
179 }
180
parseError(const QString & error) const181 void ExternalToolRunTask::parseError(const QString &error) const {
182 logParser->parseErrOutput(error);
183 }
184
185 ////////////////////////////////////////
186 // ExternalToolSupportTask
setListenerForTask(ExternalToolRunTask * runTask,int listenerNumber)187 void ExternalToolSupportTask::setListenerForTask(ExternalToolRunTask *runTask, int listenerNumber) {
188 CHECK(listeners.size() > listenerNumber, );
189 runTask->addOutputListener(listeners.at(listenerNumber));
190 }
191
setListenerForHelper(ExternalToolRunTaskHelper * helper,int listenerNumber)192 void ExternalToolSupportTask::setListenerForHelper(ExternalToolRunTaskHelper *helper, int listenerNumber) {
193 CHECK(listeners.size() > listenerNumber, );
194 helper->addOutputListener(listeners.at(listenerNumber));
195 }
196
getListener(int listenerNumber)197 ExternalToolListener *ExternalToolSupportTask::getListener(int listenerNumber) {
198 CHECK(listeners.size() > listenerNumber, nullptr);
199 return listeners.at(listenerNumber);
200 }
201
202 ////////////////////////////////////////
203 // ExternalToolRunTaskHelper
ExternalToolRunTaskHelper(ExternalToolRunTask * t)204 ExternalToolRunTaskHelper::ExternalToolRunTaskHelper(ExternalToolRunTask *t)
205 : os(t->stateInfo), logParser(t->logParser), process(t->externalToolProcess), listener(nullptr) {
206 logData.resize(1000);
207 connect(process, SIGNAL(readyReadStandardOutput()), SLOT(sl_onReadyToReadLog()));
208 connect(process, SIGNAL(readyReadStandardError()), SLOT(sl_onReadyToReadErrLog()));
209 }
210
ExternalToolRunTaskHelper(QProcess * _process,ExternalToolLogParser * _logParser,U2OpStatus & _os)211 ExternalToolRunTaskHelper::ExternalToolRunTaskHelper(QProcess *_process, ExternalToolLogParser *_logParser, U2OpStatus &_os)
212 : os(_os), logParser(_logParser), process(_process), listener(nullptr) {
213 logData.resize(1000);
214 connect(process, SIGNAL(readyReadStandardOutput()), SLOT(sl_onReadyToReadLog()));
215 connect(process, SIGNAL(readyReadStandardError()), SLOT(sl_onReadyToReadErrLog()));
216 }
217
sl_onReadyToReadLog()218 void ExternalToolRunTaskHelper::sl_onReadyToReadLog() {
219 QMutexLocker locker(&logMutex);
220
221 CHECK(nullptr != process, );
222 if (process->readChannel() == QProcess::StandardError) {
223 process->setReadChannel(QProcess::StandardOutput);
224 }
225 int numberReadChars = static_cast<int>(process->read(logData.data(), logData.size()));
226 while (numberReadChars > 0) {
227 // call log parser
228 QString line = QString::fromLocal8Bit(logData.constData(), numberReadChars);
229 logParser->parseOutput(line);
230 if (nullptr != listener) {
231 listener->addNewLogMessage(line, ExternalToolListener::OUTPUT_LOG);
232 }
233 numberReadChars = static_cast<int>(process->read(logData.data(), logData.size()));
234 }
235 os.setProgress(logParser->getProgress());
236 }
237
sl_onReadyToReadErrLog()238 void ExternalToolRunTaskHelper::sl_onReadyToReadErrLog() {
239 QMutexLocker locker(&logMutex);
240
241 CHECK(nullptr != process, );
242 if (process->readChannel() == QProcess::StandardOutput) {
243 process->setReadChannel(QProcess::StandardError);
244 }
245 int numberReadChars = static_cast<int>(process->read(logData.data(), logData.size()));
246 while (numberReadChars > 0) {
247 // call log parser
248 QString line = QString::fromLocal8Bit(logData.constData(), numberReadChars);
249 logParser->parseErrOutput(line);
250 if (nullptr != listener) {
251 listener->addNewLogMessage(line, ExternalToolListener::ERROR_LOG);
252 }
253 numberReadChars = static_cast<int>(process->read(logData.data(), logData.size()));
254 }
255 processErrorToLog();
256 os.setProgress(logParser->getProgress());
257 }
258
addOutputListener(ExternalToolListener * _listener)259 void ExternalToolRunTaskHelper::addOutputListener(ExternalToolListener *_listener) {
260 listener = _listener;
261 }
262
processErrorToLog()263 void ExternalToolRunTaskHelper::processErrorToLog() {
264 QString lastErr = logParser->getLastError();
265 if (!lastErr.isEmpty()) {
266 os.setError(lastErr);
267 }
268 }
269
270 ////////////////////////////////////////
271 // ExternalToolLogParser
ExternalToolLogParser(bool _writeErrorsToLog)272 ExternalToolLogParser::ExternalToolLogParser(bool _writeErrorsToLog) {
273 progress = -1;
274 lastLine = "";
275 lastErrLine = "";
276 lastError = "";
277 writeErrorsToLog = _writeErrorsToLog;
278 }
279
parseOutput(const QString & partOfLog)280 void ExternalToolLogParser::parseOutput(const QString &partOfLog) {
281 lastPartOfLog = partOfLog.split(QRegularExpression("\\r?\\n"));
282 lastPartOfLog.first() = lastLine + lastPartOfLog.first();
283 // It's a possible situation, that one message will be processed twice
284 lastLine = lastPartOfLog.last();
285 foreach (const QString &buf, lastPartOfLog) {
286 processLine(buf);
287 }
288 }
289
parseErrOutput(const QString & partOfLog)290 void ExternalToolLogParser::parseErrOutput(const QString &partOfLog) {
291 lastPartOfLog = partOfLog.split(QRegularExpression("\\r?\\n"));
292 lastPartOfLog.first() = lastErrLine + lastPartOfLog.first();
293 // It's a possible situation, that one message will be processed twice
294 lastErrLine = lastPartOfLog.last();
295 foreach (const QString &buf, lastPartOfLog) {
296 processErrLine(buf);
297 }
298 }
299
processLine(const QString & line)300 void ExternalToolLogParser::processLine(const QString &line) {
301 if (isError(line)) {
302 setLastError(line);
303 } else {
304 ioLog.trace(line);
305 }
306 }
307
processErrLine(const QString & line)308 void ExternalToolLogParser::processErrLine(const QString &line) {
309 if (isError(line)) {
310 setLastError(line);
311 } else {
312 ioLog.trace(line);
313 }
314 }
315
isError(const QString & line) const316 bool ExternalToolLogParser::isError(const QString &line) const {
317 return line.contains("error", Qt::CaseInsensitive);
318 }
319
setLastError(const QString & value)320 void ExternalToolLogParser::setLastError(const QString &value) {
321 if (!value.isEmpty() && writeErrorsToLog) {
322 ioLog.error(value);
323 }
324 lastError = value;
325 }
326
327 ////////////////////////////////////////
328 // ExternalToolSupportUtils
removeTmpDir(const QString & tmpDirUrl,U2OpStatus & os)329 void ExternalToolSupportUtils::removeTmpDir(const QString &tmpDirUrl, U2OpStatus &os) {
330 if (tmpDirUrl.isEmpty()) {
331 os.setError(tr("Can not remove temporary folder: path is empty."));
332 return;
333 }
334 QDir tmpDir(tmpDirUrl);
335 foreach (const QString &file, tmpDir.entryList(QDir::NoDotAndDotDot | QDir::AllEntries)) {
336 if (!tmpDir.remove(file)) {
337 os.setError(tr("Can not remove files from temporary folder."));
338 return;
339 }
340 }
341 if (!tmpDir.rmdir(tmpDir.absolutePath())) {
342 os.setError(tr("Can not remove folder for temporary files."));
343 }
344 }
345
createTmpDir(const QString & prePath,const QString & domain,U2OpStatus & os)346 QString ExternalToolSupportUtils::createTmpDir(const QString &prePath, const QString &domain, U2OpStatus &os) {
347 int i = 0;
348 while (true) {
349 QString tmpDirName = QString("d_%1").arg(i);
350 QString tmpDirPath = prePath + "/" + domain + "/" + tmpDirName;
351 QDir tmpDir(tmpDirPath);
352
353 if (!tmpDir.exists()) {
354 if (!QDir().mkpath(tmpDirPath)) {
355 os.setError(tr("Can not create folder for temporary files: %1").arg(tmpDirPath));
356 }
357 return tmpDir.absolutePath();
358 }
359 i++;
360 }
361 }
362
createTmpDir(const QString & domain,U2OpStatus & os)363 QString ExternalToolSupportUtils::createTmpDir(const QString &domain, U2OpStatus &os) {
364 QString tmpDirPath = AppContext::getAppSettings()->getUserAppsSettings()->getCurrentProcessTemporaryDirPath();
365 return createTmpDir(tmpDirPath, domain, os);
366 }
367
appendExistingFile(const QString & path,QStringList & files)368 void ExternalToolSupportUtils::appendExistingFile(const QString &path, QStringList &files) {
369 GUrl url(path);
370 if (QFile::exists(url.getURLString())) {
371 files << url.getURLString();
372 }
373 }
374
startExternalProcess(QProcess * process,const QString & program,const QStringList & arguments)375 bool ExternalToolSupportUtils::startExternalProcess(QProcess *process, const QString &program, const QStringList &arguments) {
376 process->start(program, arguments);
377 bool started = process->waitForStarted(START_WAIT_MSEC);
378
379 #ifdef Q_OS_WIN32
380 if (!started) {
381 QString execStr = WIN_LAUNCH_CMD_COMMAND + program;
382 foreach (const QString arg, arguments) {
383 execStr += " " + arg;
384 }
385 process->start(execStr);
386 coreLog.trace(tr("Can't run an executable file \"%1\" as it is. Try to run it as a cmd line command: \"%2\"")
387 .arg(program)
388 .arg(execStr));
389 started = process->waitForStarted(START_WAIT_MSEC);
390 }
391 #endif
392
393 return started;
394 }
395
prepareProcess(const QString & toolId,const QStringList & arguments,const QString & workingDirectory,const QStringList & additionalPaths,U2OpStatus & os,ExternalToolListener * listener)396 ProcessRun ExternalToolSupportUtils::prepareProcess(const QString &toolId, const QStringList &arguments, const QString &workingDirectory, const QStringList &additionalPaths, U2OpStatus &os, ExternalToolListener *listener) {
397 ProcessRun result;
398 result.process = nullptr;
399 result.arguments = arguments;
400
401 ExternalTool *tool = AppContext::getExternalToolRegistry()->getById(toolId);
402 CHECK_EXT(nullptr != tool, os.setError(tr("A tool with the ID %1 is absent").arg(toolId)), result);
403
404 const QString toolName = tool->getName();
405 if (tool->getPath().isEmpty()) {
406 os.setError(tr("Path for '%1' tool not set").arg(toolName));
407 return result;
408 }
409 result.program = tool->getPath();
410 QString toolRunnerProgram = tool->getToolRunnerProgramId();
411
412 if (!toolRunnerProgram.isEmpty()) {
413 ScriptingToolRegistry *stregister = AppContext::getScriptingToolRegistry();
414 SAFE_POINT_EXT(nullptr != stregister, os.setError("No scripting tool registry"), result);
415 ScriptingTool *stool = stregister->getById(toolRunnerProgram);
416 if (nullptr == stool || stool->getPath().isEmpty()) {
417 os.setError(QString("The tool %1 that runs %2 is not installed. Please set the path of the tool in the External Tools settings").arg(toolRunnerProgram).arg(toolName));
418 return result;
419 }
420 result.arguments.prepend(result.program);
421
422 for (int i = stool->getRunParameters().size() - 1; i >= 0; i--) {
423 result.arguments.prepend(stool->getRunParameters().at(i));
424 }
425 foreach (const QString ¶m, tool->getToolRunnerAdditionalOptions()) {
426 result.arguments.prepend(param);
427 }
428 result.program = stool->getPath();
429 }
430
431 #ifdef Q_OS_WIN
432 const QString pathVariableSeparator = ";";
433 #else
434 const QString pathVariableSeparator = ":";
435 #endif
436
437 QProcessEnvironment processEnvironment = QProcessEnvironment::systemEnvironment();
438 QString path = additionalPaths.join(pathVariableSeparator) + pathVariableSeparator +
439 tool->getAdditionalPaths().join(pathVariableSeparator) + pathVariableSeparator +
440 processEnvironment.value("PATH");
441 if (!additionalPaths.isEmpty()) {
442 algoLog.trace(QString("PATH environment variable: '%1'").arg(path));
443 }
444 processEnvironment.insert("PATH", path);
445
446 result.process = new QProcess();
447 result.process->setProcessEnvironment(processEnvironment);
448 if (!workingDirectory.isEmpty()) {
449 result.process->setWorkingDirectory(workingDirectory);
450 algoLog.details(tr("Working folder is \"%1\"").arg(result.process->workingDirectory()));
451 }
452
453 // QProcess wraps arguments that contain spaces in quotes automatically.
454 // But launched line should look correctly in the log.
455 const QString commandWithArguments = GUrlUtils::getQuotedString(result.program) + ExternalToolSupportUtils::prepareArgumentsForCmdLine(result.arguments);
456 algoLog.details(tr("Launching %1 tool: %2").arg(toolName).arg(commandWithArguments));
457
458 if (nullptr != listener) {
459 listener->setToolName(toolName);
460 listener->addNewLogMessage(commandWithArguments, ExternalToolListener::PROGRAM_WITH_ARGUMENTS);
461 }
462 return result;
463 }
464
prepareArgumentsForCmdLine(const QStringList & arguments)465 QString ExternalToolSupportUtils::prepareArgumentsForCmdLine(const QStringList &arguments) {
466 QString argumentsLine;
467 foreach (QString argumentStr, arguments) {
468 // Find start of the parameter value
469 int startIndex = argumentStr.indexOf('=') + 1;
470 // Add quotes if parameter contains whitespace characters
471 QString valueStr = argumentStr.mid(startIndex);
472 if (valueStr.contains(' ') || valueStr.contains('\t')) {
473 argumentStr.append('"');
474 argumentStr.insert(startIndex, '"');
475 }
476 argumentsLine += ' ' + argumentStr;
477 }
478 return argumentsLine;
479 }
480
splitCmdLineArguments(const QString & program)481 QStringList ExternalToolSupportUtils::splitCmdLineArguments(const QString &program) {
482 // a function body from "qprocess.cpp"
483
484 QStringList args;
485 QString tmp;
486 int quoteCount = 0;
487 bool inQuote = false;
488
489 // handle quoting. tokens can be surrounded by double quotes
490 // "hello world". three consecutive double quotes represent
491 // the quote character itself.
492 for (int i = 0; i < program.size(); ++i) {
493 if (program.at(i) == QLatin1Char('"') || program.at(i) == QLatin1Char('\'')) {
494 ++quoteCount;
495 if (quoteCount == 3) {
496 // third consecutive quote
497 quoteCount = 0;
498 tmp += program.at(i);
499 }
500 continue;
501 }
502 if (quoteCount) {
503 if (quoteCount == 1)
504 inQuote = !inQuote;
505 quoteCount = 0;
506 }
507 if (!inQuote && program.at(i).isSpace()) {
508 if (!tmp.isEmpty()) {
509 args += tmp;
510 tmp.clear();
511 }
512 } else {
513 tmp += program.at(i);
514 }
515 }
516 if (!tmp.isEmpty())
517 args += tmp;
518
519 return args;
520 }
521
getScoresGapDependencyMap()522 QVariantMap ExternalToolSupportUtils::getScoresGapDependencyMap() {
523 QVariantMap map;
524 QVariantMap gaps;
525 gaps["2 2"] = "2 2";
526 gaps["1 2"] = "1 2";
527 gaps["0 2"] = "0 2";
528 gaps["2 1"] = "2 1";
529 gaps["1 1"] = "1 1";
530 map.insert("1 -4", gaps);
531 map.insert("1 -3", gaps);
532
533 gaps.clear();
534 gaps["2 2"] = "2 2";
535 gaps["1 2"] = "1 2";
536 gaps["0 2"] = "0 2";
537 gaps["3 1"] = "3 1";
538 gaps["2 1"] = "2 1";
539 gaps["1 1"] = "1 1";
540 map.insert("1 -2", gaps);
541
542 gaps.clear();
543 gaps["4 2"] = "4 2";
544 gaps["3 2"] = "3 2";
545 gaps["2 2"] = "2 2";
546 gaps["1 2"] = "1 2";
547 gaps["0 2"] = "0 2";
548 gaps["4 1"] = "4 1";
549 gaps["3 1"] = "3 1";
550 gaps["2 1"] = "2 1";
551 map.insert("1 -1", gaps);
552
553 gaps.clear();
554 gaps["4 4"] = "4 4";
555 gaps["2 4"] = "2 4";
556 gaps["0 4"] = "0 4";
557 gaps["4 2"] = "4 2";
558 gaps["2 2"] = "2 2";
559 map.insert("2 -7", gaps);
560 map.insert("2 -5", gaps);
561
562 gaps.clear();
563 gaps["6 4"] = "6 4";
564 gaps["4 4"] = "4 4";
565 gaps["2 4"] = "2 4";
566 gaps["0 4"] = "0 4";
567 gaps["3 3"] = "3 3";
568 gaps["6 2"] = "6 2";
569 gaps["5 2"] = "5 2";
570 gaps["4 2"] = "4 2";
571 gaps["2 2"] = "2 2";
572 map.insert("2 -3", gaps);
573
574 gaps.clear();
575 gaps["12 8"] = "12 8";
576 gaps["6 5"] = "6 5";
577 gaps["5 5"] = "5 5";
578 gaps["4 5"] = "4 5";
579 gaps["3 5"] = "3 5";
580 map.insert("4 -5", gaps);
581 map.insert("5 -4", gaps);
582
583 return map;
584 }
585
~ExternalToolLogProcessor()586 ExternalToolLogProcessor::~ExternalToolLogProcessor() {
587 }
588
ExternalToolListener(ExternalToolLogProcessor * logProcessor)589 ExternalToolListener::ExternalToolListener(ExternalToolLogProcessor *logProcessor)
590 : logProcessor(logProcessor) {
591 }
592
~ExternalToolListener()593 ExternalToolListener::~ExternalToolListener() {
594 delete logProcessor;
595 }
596
setToolName(const QString & _toolName)597 void ExternalToolListener::setToolName(const QString &_toolName) {
598 toolName = _toolName;
599 }
600
setLogProcessor(ExternalToolLogProcessor * newLogProcessor)601 void ExternalToolListener::setLogProcessor(ExternalToolLogProcessor *newLogProcessor) {
602 delete logProcessor;
603 logProcessor = newLogProcessor;
604 }
605
getToolName() const606 const QString &ExternalToolListener::getToolName() const {
607 return toolName;
608 }
609
610 } // namespace U2
611