diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 00bb99ac80741fc50ef8a289719373032f2391eb (patch) | |
tree | 3a5a9bf72f942784b38bf77dd66c534662fab5f2 /kttsd/filters/xmltransformer/xmltransformerproc.cpp | |
download | tdeaccessibility-00bb99ac80741fc50ef8a289719373032f2391eb.tar.gz tdeaccessibility-00bb99ac80741fc50ef8a289719373032f2391eb.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdeaccessibility@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kttsd/filters/xmltransformer/xmltransformerproc.cpp')
-rw-r--r-- | kttsd/filters/xmltransformer/xmltransformerproc.cpp | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/kttsd/filters/xmltransformer/xmltransformerproc.cpp b/kttsd/filters/xmltransformer/xmltransformerproc.cpp new file mode 100644 index 0000000..d4aa1c5 --- /dev/null +++ b/kttsd/filters/xmltransformer/xmltransformerproc.cpp @@ -0,0 +1,385 @@ +/***************************************************** vim:set ts=4 sw=4 sts=4: + Generic XML Transformation Filter Processing class. + ------------------- + Copyright: + (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net> + ------------------- + Original author: Gary Cramblitt <garycramblitt@comcast.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ******************************************************************************/ + +// Qt includes. +#include <qfile.h> +#include <qregexp.h> + +// KDE includes. +#include <kdeversion.h> +#include <kconfig.h> +#include <ktempfile.h> +#include <kstandarddirs.h> +#include <kprocess.h> +#include <kdebug.h> + +// KTTS includes. +#include "filterproc.h" +#include "utils.h" + +// XmlTransformer includes. +#include "xmltransformerproc.h" +#include "xmltransformerproc.moc" + +/** + * Constructor. + */ +XmlTransformerProc::XmlTransformerProc( QObject *parent, const char *name, const QStringList& ) : + KttsFilterProc(parent, name) +{ + m_xsltProc = 0; +} + +/** + * Destructor. + */ +/*virtual*/ XmlTransformerProc::~XmlTransformerProc() +{ + delete m_xsltProc; + if (!m_inFilename.isEmpty()) QFile::remove(m_inFilename); + if (!m_outFilename.isEmpty()) QFile::remove(m_outFilename); +} + +/** + * Initialize the filter. + * @param config Settings object. + * @param configGroup Settings Group. + * @return False if filter is not ready to filter. + * + * Note: The parameters are for reading from kttsdrc file. Plugins may wish to maintain + * separate configuration files of their own. + */ +bool XmlTransformerProc::init(KConfig* config, const QString& configGroup) +{ + // kdDebug() << "XmlTransformerProc::init: Running." << endl; + config->setGroup( configGroup ); + m_UserFilterName = config->readEntry( "UserFilterName" ); + m_xsltFilePath = config->readEntry( "XsltFilePath" ); + m_xsltprocPath = config->readEntry( "XsltprocPath" ); + m_rootElementList = config->readListEntry( "RootElement", ',' ); + m_doctypeList = config->readListEntry( "DocType", ',' ); + m_appIdList = config->readListEntry( "AppID", ',' ); + kdDebug() << "XmlTransformerProc::init: m_xsltprocPath = " << m_xsltprocPath << endl; + kdDebug() << "XmlTransformerProc::init: m_xsltFilePath = " << m_xsltFilePath << endl; + return ( m_xsltFilePath.isEmpty() || m_xsltprocPath.isEmpty() ); +} + +/** + * Returns True if the plugin supports asynchronous processing, + * i.e., supports asyncConvert method. + * @return True if this plugin supports asynchronous processing. + * + * If the plugin returns True, it must also implement @ref getState . + * It must also emit @ref filteringFinished when filtering is completed. + * If the plugin returns True, it must also implement @ref stopFiltering . + * It must also emit @ref filteringStopped when filtering has been stopped. + */ +/*virtual*/ bool XmlTransformerProc::supportsAsync() { return true; } + +/** + * Convert input, returning output. + * @param inputText Input text. + * @param talkerCode TalkerCode structure for the talker that KTTSD intends to + * use for synthing the text. Useful for extracting hints about + * how to filter the text. For example, languageCode. + * @param appId The DCOP appId of the application that queued the text. + * Also useful for hints about how to do the filtering. + */ +/*virtual*/ QString XmlTransformerProc::convert(const QString& inputText, TalkerCode* talkerCode, + const QCString& appId) +{ + // kdDebug() << "XmlTransformerProc::convert: Running." << endl; + // If not properly configured, do nothing. + if ( m_xsltFilePath.isEmpty() || m_xsltprocPath.isEmpty() ) + { + kdDebug() << "XmlTransformerProc::convert: not properly configured" << endl; + return inputText; + } + // Asynchronously convert and wait for completion. + if (asyncConvert(inputText, talkerCode, appId)) + { + waitForFinished(); + m_state = fsIdle; + return m_text; + } else + return inputText; +} + +/** + * Convert input. Runs asynchronously. + * @param inputText Input text. + * @param talkerCode TalkerCode structure for the talker that KTTSD intends to + * use for synthing the text. Useful for extracting hints about + * how to filter the text. For example, languageCode. + * @param appId The DCOP appId of the application that queued the text. + * Also useful for hints about how to do the filtering. + * @return False if the filter cannot perform the conversion. + * + * When conversion is completed, emits signal @ref filteringFinished. Calling + * program may then call @ref getOutput to retrieve converted text. Calling + * program must call @ref ackFinished to acknowledge the conversion. + */ +/*virtual*/ bool XmlTransformerProc::asyncConvert(const QString& inputText, TalkerCode* /*talkerCode*/, + const QCString& appId) +{ + m_wasModified = false; + + // kdDebug() << "XmlTransformerProc::asyncConvert: Running." << endl; + m_text = inputText; + // If not properly configured, do nothing. + if ( m_xsltFilePath.isEmpty() || m_xsltprocPath.isEmpty() ) + { + kdDebug() << "XmlTransformerProc::asyncConvert: not properly configured." << endl; + return false; + } + + bool found = false; + // If not correct XML type, or DOCTYPE, do nothing. + if ( !m_rootElementList.isEmpty() ) + { + // kdDebug() << "XmlTransformerProc::asyncConvert:: searching for root elements " << m_rootElementList << endl; + for ( uint ndx=0; ndx < m_rootElementList.count(); ++ndx ) + { + if ( KttsUtils::hasRootElement( inputText, m_rootElementList[ndx] ) ) + { + found = true; + break; + } + } + if ( !found && m_doctypeList.isEmpty() ) + { + kdDebug() << "XmlTransformerProc::asyncConvert: Did not find root element(s)" << m_rootElementList << endl; + return false; + } + } + if ( !found && !m_doctypeList.isEmpty() ) + { + for ( uint ndx=0; ndx < m_doctypeList.count(); ++ndx ) + { + if ( KttsUtils::hasDoctype( inputText, m_doctypeList[ndx] ) ) + { + found = true; + break; + } + } + if ( !found ) + { + // kdDebug() << "XmlTransformerProc::asyncConvert: Did not find doctype(s)" << m_doctypeList << endl; + return false; + } + } + + // If appId doesn't match, return input unmolested. + if ( !m_appIdList.isEmpty() ) + { + QString appIdStr = appId; + // kdDebug() << "XmlTransformrProc::convert: converting " << inputText << " if appId " + // << appId << " matches " << m_appIdList << endl; + found = false; + for ( uint ndx=0; ndx < m_appIdList.count(); ++ndx ) + { + if ( appIdStr.contains(m_appIdList[ndx]) ) + { + found = true; + break; + } + } + if ( !found ) + { + // kdDebug() << "XmlTransformerProc::asyncConvert: Did not find appId(s)" << m_appIdList << endl; + return false; + } + } + + /// Write @param text to a temporary file. + KTempFile inFile(locateLocal("tmp", "kttsd-"), ".xml"); + m_inFilename = inFile.file()->name(); + QTextStream* wstream = inFile.textStream(); + if (wstream == 0) { + /// wtf... + kdDebug() << "XmlTransformerProc::convert: Can't write to " << m_inFilename << endl;; + return false; + } + // TODO: Is encoding an issue here? + // If input does not have xml processing instruction, add it. + if (!inputText.startsWith("<?xml")) *wstream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; + // FIXME: Temporary Fix until Konqi returns properly formatted xhtml with & coded as & + // This will change & inside a CDATA section, which is not good, and also within comments and + // processing instructions, which is OK because we don't speak those anyway. + QString text = inputText; + text.replace(QRegExp("&(?!amp;)"),"&"); + *wstream << text; + inFile.close(); +#if KDE_VERSION >= KDE_MAKE_VERSION (3,3,0) + inFile.sync(); +#endif + + // Get a temporary output file name. + KTempFile outFile(locateLocal("tmp", "kttsd-"), ".output"); + m_outFilename = outFile.file()->name(); + outFile.close(); + // outFile.unlink(); // only activate this if necessary. + + /// Spawn an xsltproc process to apply our stylesheet to input file. + m_xsltProc = new KProcess; + *m_xsltProc << m_xsltprocPath; + *m_xsltProc << "-o" << m_outFilename << "--novalid" + << m_xsltFilePath << m_inFilename; + // Warning: This won't compile under KDE 3.2. See FreeTTS::argsToStringList(). + // kdDebug() << "SSMLConvert::transform: executing command: " << + // m_xsltProc->args() << endl; + + m_state = fsFiltering; + connect(m_xsltProc, SIGNAL(processExited(KProcess*)), + this, SLOT(slotProcessExited(KProcess*))); + connect(m_xsltProc, SIGNAL(receivedStdout(KProcess*, char*, int)), + this, SLOT(slotReceivedStdout(KProcess*, char*, int))); + connect(m_xsltProc, SIGNAL(receivedStderr(KProcess*, char*, int)), + this, SLOT(slotReceivedStderr(KProcess*, char*, int))); + if (!m_xsltProc->start(KProcess::NotifyOnExit, + static_cast<KProcess::Communication>(KProcess::Stdout | KProcess::Stderr))) + { + kdDebug() << "XmlTransformerProc::convert: Error starting xsltproc" << endl; + m_state = fsIdle; + return false; + } + return true; +} + +// Process output when xsltproc exits. +void XmlTransformerProc::processOutput() +{ + QFile::remove(m_inFilename); + + int exitStatus = 11; + if (m_xsltProc->normalExit()) + exitStatus = m_xsltProc->exitStatus(); + else + kdDebug() << "XmlTransformerProc::processOutput: xsltproc was killed." << endl; + + delete m_xsltProc; + m_xsltProc = 0; + + if (exitStatus != 0) + { + kdDebug() << "XmlTransformerProc::processOutput: xsltproc abnormal exit. Status = " << exitStatus << endl; + m_state = fsFinished; + QFile::remove(m_outFilename); + emit filteringFinished(); + return; + } + + /// Read back the data that was written to /tmp/fileName.output. + QFile readfile(m_outFilename); + if(!readfile.open(IO_ReadOnly)) { + /// uhh yeah... Issues writing to the output file. + kdDebug() << "XmlTransformerProc::processOutput: Could not read file " << m_outFilename << endl; + m_state = fsFinished; + emit filteringFinished(); + } + QTextStream rstream(&readfile); + m_text = rstream.read(); + readfile.close(); + + kdDebug() << "XmlTransformerProc::processOutput: Read file at " + m_inFilename + " and created " + m_outFilename + " based on the stylesheet at " << m_xsltFilePath << endl; + + // Clean up. + QFile::remove(m_outFilename); + + m_state = fsFinished; + m_wasModified = true; + emit filteringFinished(); +} + +/** + * Waits for a previous call to asyncConvert to finish. + */ +/*virtual*/ void XmlTransformerProc::waitForFinished() +{ + if (m_xsltProc) + { + if (m_xsltProc->isRunning()) + { + if ( !m_xsltProc->wait( 15 ) ) + { + m_xsltProc->kill(); + kdDebug() << "XmlTransformerProc::waitForFinished: After waiting 15 seconds, xsltproc process seems to hung. Killing it." << endl; + processOutput(); + } + } + } +} + +/** + * Returns the state of the Filter. + */ +/*virtual*/ int XmlTransformerProc::getState() { return m_state; } + +/** + * Returns the filtered output. + */ +/*virtual*/ QString XmlTransformerProc::getOutput() { return m_text; } + +/** + * Acknowledges the finished filtering. + */ +/*virtual*/ void XmlTransformerProc::ackFinished() +{ + m_state = fsIdle; + m_text = QString::null; +} + +/** + * Stops filtering. The filteringStopped signal will emit when filtering + * has in fact stopped and state returns to fsIdle; + */ +/*virtual*/ void XmlTransformerProc::stopFiltering() +{ + m_state = fsStopping; + m_xsltProc->kill(); +} + +/** + * Did this filter do anything? If the filter returns the input as output + * unmolested, it should return False when this method is called. + */ +/*virtual*/ bool XmlTransformerProc::wasModified() { return m_wasModified; } + +void XmlTransformerProc::slotProcessExited(KProcess*) +{ + // kdDebug() << "XmlTransformerProc::slotProcessExited: xsltproc has exited." << endl; + processOutput(); +} + +void XmlTransformerProc::slotReceivedStdout(KProcess*, char* /*buffer*/, int /*buflen*/) +{ + // QString buf = QString::fromLatin1(buffer, buflen); + // kdDebug() << "XmlTransformerProc::slotReceivedStdout: Received from xsltproc: " << buf << endl; +} + +void XmlTransformerProc::slotReceivedStderr(KProcess*, char* buffer, int buflen) +{ + QString buf = QString::fromLatin1(buffer, buflen); + kdDebug() << "XmlTransformerProc::slotReceivedStderr: Received error from xsltproc: " << buf << endl; +} + |