/*##############################################################################
Copyright (C) 2011 HPCC Systems.
All rights reserved. This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
############################################################################## */
#include "xslprocessor.ipp"
#include "jencrypt.hpp"
#include "jexcept.hpp"
#include "xalanc/XPath/XObjectFactory.hpp"
#include "xalanc/PlatformSupport/Writer.hpp"
/*
class StringBufferOStreamBuf : public std::basic_streambuf >
{
StringBuffer & _inputbuffer;
typedef std::char_traits _Tr;
protected:
virtual int overflow(int = _Tr::eof());
public:
StringBufferOStreamBuf(StringBuffer &_str);
};
StringBufferOStreamBuf::StringBufferOStreamBuf(StringBuffer &_str) : _inputbuffer(_str)
{
}
int StringBufferOStreamBuf::overflow(int c)
{
if(c == _Tr::eof()) return _Tr::not_eof(c);
_inputbuffer.append((char) c);
return c;
}
class StringBufferOStream : public std::basic_ostream >
{
StringBufferOStreamBuf _streambuffer;
public:
StringBufferOStream(StringBuffer &buf)
: _streambuffer(buf),
std::basic_ostream >(&_streambuffer)
{ clear(); }
};
*/
//-------------------------------------------------
XALAN_USING_XALAN(Writer)
XALAN_USING_XALAN(XalanOutputStream)
XALAN_USING_XALAN(XalanDOMChar)
class ISocketOutputStream : public IInterface
{
public:
virtual Writer* getWriter() = 0;
};
//XALAN_CPP_NAMESPACE_BEGIN
class SocketOutputStream : public CInterface, public ISocketOutputStream, public Writer
{
private:
ISocket *m_socket;
public:
IMPLEMENT_IINTERFACE;
SocketOutputStream(ISocket *s);
~SocketOutputStream() { close(); }
virtual Writer* getWriter() { return this; }
// Close the stream
virtual void close();
// Flush the stream
virtual void flush();
// Get the stream associated with the writer...
virtual XalanOutputStream* getStream();
// Get the stream associated with the writer...
virtual const XalanOutputStream* getStream() const;
// Writes a string
virtual void write(const char* s, size_t theOffset = 0, size_t theLength = ~0u);
// Writes a string
virtual void write(const XalanDOMChar* s, XalanDOMString::size_type theOffset=0,
XalanDOMString::size_type theLength = XalanDOMString::npos);
// Writes a character
virtual void write(XalanDOMChar c);
// Writes a string
virtual void write(const XalanDOMString& s, XalanDOMString::size_type theOffset = 0,
XalanDOMString::size_type theLength = XalanDOMString::npos);
};
SocketOutputStream::SocketOutputStream(ISocket* s)
{
m_socket = s;
}
void SocketOutputStream::close()
{
//fprintf(stderr,"close()\n");
//m_socket->shutdown();
//m_socket->close();
}
void SocketOutputStream::flush()
{
//fprintf(stderr,"flush()\n");
}
XalanOutputStream* SocketOutputStream::getStream()
{
fprintf(stderr, "Unsupported getStream()!");
return NULL;
}
const XalanOutputStream* SocketOutputStream::getStream() const
{
fprintf(stderr, "Unsupported getStream()!");
return NULL;
}
void SocketOutputStream::write(const char* s, size_t offset, size_t length)
{
//wprintf(stderr,TEXT("write(char*='%s',offset=%d,length=%d)\n"),s,offset,length);
m_socket->write(s + offset, length);
}
//XalanDOMChar: utf-8 or wchar_t
void SocketOutputStream::write(const XalanDOMChar* s, XalanDOMString::size_type offset,
XalanDOMString::size_type length)
{
//printf(stderr,"write(DOMChar='%s',offset=%d,length=%d)\n",s,offset,length);
m_socket->write((const char* )(s+offset), length * sizeof(XalanDOMChar));
}
void SocketOutputStream::write(XalanDOMChar c)
{
//printf(stderr, "write(%c)", c);
m_socket->write((const char*)&c, sizeof(XalanDOMChar));
}
void SocketOutputStream::write(const XalanDOMString& s, XalanDOMString::size_type offset,
XalanDOMString::size_type length)
{
//printf(stderr,"write(DOMString='%s',offset=%d,length=%s", s.c_str(), offset, length);
m_socket->write((const char*)(s.c_str()+offset), length*sizeof(XalanDOMChar));
}
extern ISocketOutputStream* createSocketOutputStream(ISocket* s)
{
return new SocketOutputStream(s);
}
//-------------------------------------------------
class XalanStringBufferOutputHandler
{
StringBuffer & _inputbuffer;
public:
XalanStringBufferOutputHandler(StringBuffer &_str) : _inputbuffer(_str)
{
}
static unsigned long callback(const char *data, unsigned long len, void *ctx)
{
XalanStringBufferOutputHandler *self = (XalanStringBufferOutputHandler *) ctx;
self->_inputbuffer.append(len, data);
return len;
}
};
//----------------------------------------------------------------------------
// CExternalFunction
//----------------------------------------------------------------------------
XObjectPtr CExternalFunction::execute( XPathExecutionContext& executionContext,
XalanNode* /* context */,
const XObjectPtr arg1,
const Locator* /* locator */) const
{
assert(arg1.null() == false);
const XalanDOMString& arg = arg1->str();
//convert XalanDOMString (implemented as unsigned short*) into StringBuffer
StringBuffer sbInput;
sbInput.ensureCapacity(arg.length()+1);
size32_t len = arg.length();
for (int i=0; i < len; i++)
sbInput.append( (char) arg[i]);
StringBuffer sbOutput;
try
{
(*m_userFunction)(sbOutput, sbInput.str(), m_pTransform);
}
catch (IException* e)
{
StringBuffer msg;
e->errorMessage(msg);
e->Release();
}
catch (...)
{
}
XalanDOMString xdsOutput( sbOutput.str() );
return executionContext.getXObjectFactory().createString( xdsOutput );
}
//----------------------------------------------------------------------------
// CXslProcessor
//----------------------------------------------------------------------------
extern IXslProcessor* getXslProcessor()
{
static CXslProcessor s_pXslProcessor;
return LINK(&s_pXslProcessor);
}
// Should be called only once per-process
class XslProcessorInitializer
{
public:
XslProcessorInitializer()
{
// Call the static initializer for Xerces.
XMLPlatformUtils::Initialize();
// Initialize Xalan.
XalanTransformer::initialize();
}
~XslProcessorInitializer()
{
// Terminate Xalan.
XalanTransformer::terminate();
// Call the static terminator for Xerces.
XMLPlatformUtils::Terminate();
}
};
CXslProcessor::CXslProcessor()
{
static XslProcessorInitializer initializer;
m_cachetimeout = XSLT_DEFAULT_CACHETIMEOUT;
}
// Should be called only once per-process
CXslProcessor::~CXslProcessor()
{
}
IXslTransform *CXslProcessor::createXslTransform()
{
return new CXslTransform(inch.get());
}
int CXslProcessor::execute(IXslTransform *pITransform)
{
return ((CXslTransform*)pITransform)->transform();
}
void CXslProcessor::setCacheTimeout(int timeout)
{
m_cachetimeout = timeout;
IXslCache* xslcache = getXslCache();
if(xslcache)
xslcache->setCacheTimeout(timeout);
}
int CXslProcessor::getCacheTimeout()
{
return m_cachetimeout;
}
//----------------------------------------------------------------------------
// CXslTransform
//----------------------------------------------------------------------------
/*static*/ const char* CXslTransform::SEISINT_NAMESPACE = "http://seisint.com";
CXslTransform::CXslTransform(IIncludeHandler* handler) : m_XalanTransformer()
{
m_ParsedSource = 0;
m_resultTarget = 0;
m_ostrstream = 0;
m_sourceResolver = NULL;
m_pUserData = NULL;
#ifdef _WIN32
m_normalizeLinefeed = true; //default for Xalan
#endif
if (handler)
setIncludeHandler(handler);
//set an external function to handle non-fatal XSL messages
m_fnMessage.setown(createExternalFunction("message", message));
setExternalFunction(SEISINT_NAMESPACE, m_fnMessage.get(), true);
}
CXslTransform::~CXslTransform()
{
setExternalFunction(SEISINT_NAMESPACE, m_fnMessage.get(), false);
if(m_sourceResolver != NULL)
delete m_sourceResolver;
if (m_xslsource)
m_xslsource->clearIncludeHandler();
if(m_ParsedSource)
{
m_XalanTransformer.destroyParsedSource(m_ParsedSource);
m_ParsedSource = NULL;
}
closeResultTarget();
}
bool CXslTransform::checkSanity()
{
return (m_xslsource && m_ParsedSource);
}
int CXslTransform::transform(StringBuffer &target)
{
if(!m_ParsedSource)
throw MakeStringException(1, "[XML source not set]");
else if(!m_xslsource)
throw MakeStringException(2, "[XSL stylesheet not set]");
XalanCompiledStylesheet* pCompiledStylesheet = NULL;
pCompiledStylesheet = m_xslsource->getStylesheet();
if (!pCompiledStylesheet)
{
DBGLOG("[failed to compile XSLT stylesheet]");
throw MakeStringException(2, "[failed to compile XSLT stylesheet]");
}
int rc=0;
m_sMessages.clear();
try
{
XalanStringBufferOutputHandler output(target);
rc = m_XalanTransformer.transform(*m_ParsedSource, pCompiledStylesheet,
(void*)&output, (XalanOutputHandlerType)output.callback, (XalanFlushHandlerType)0);
}
catch(...)
{
StringBuffer estr("[Exception running XSLT stylesheet]");
estr.appendf("[%s]", m_XalanTransformer.getLastError());
DBGLOG("%s", estr.str());
throw MakeStringException(2, "%s", estr.str());
}
if (rc < 0)
{
StringBuffer estr;
estr.appendf("[%s]", m_XalanTransformer.getLastError());
DBGLOG("%s", estr.str());
throw MakeStringException(2, "%s", estr.str());
}
return rc;
}
int CXslTransform::transform()
{
if(!m_ParsedSource)
throw MakeStringException(1, "[XML source not set for XSLT[");
else if(!m_xslsource)
throw MakeStringException(2, "[XSL stylesheet not set]");
else if(!m_resultTarget)
throw MakeStringException(2, "[XSLT target file/buffer not set]");
XalanCompiledStylesheet* pCompiledStylesheet = NULL;
pCompiledStylesheet = m_xslsource->getStylesheet();
if (!pCompiledStylesheet)
{
DBGLOG("[failed to compile XSLT stylesheet]");
throw MakeStringException(2, "[failed to compile XSLT stylesheet]");
}
int rc=0;
m_sMessages.clear();
try
{
rc = m_XalanTransformer.transform(*m_ParsedSource, pCompiledStylesheet, *m_resultTarget);
}
catch(...)
{
StringBuffer estr("[Exception running XSLT stylesheet]");
estr.appendf("[%s]", m_XalanTransformer.getLastError());
DBGLOG("%s", estr.str());
throw MakeStringException(2, "%s", estr.str());
}
if (rc < 0)
{
StringBuffer estr;
estr.appendf("[%s]", m_XalanTransformer.getLastError());
DBGLOG("%s", estr.str());
throw MakeStringException(2, "%s", estr.str());
}
return rc;
}
int CXslTransform::transform(ISocket* targetSocket)
{
if(!m_ParsedSource)
throw MakeStringException(1, "[XML source not set for XSLT[");
else if(!m_xslsource)
throw MakeStringException(2, "[XSL stylesheet not set]");
XalanCompiledStylesheet* pCompiledStylesheet = NULL;
pCompiledStylesheet = m_xslsource->getStylesheet();
if (!pCompiledStylesheet)
{
DBGLOG("[failed to compile XSLT stylesheet]");
throw MakeStringException(2, "[failed to compile XSLT stylesheet]");
}
int rc=0;
m_sMessages.clear();
try
{
m_resultTarget = new XSLTResultTarget();
Owned stream = createSocketOutputStream(targetSocket);
m_resultTarget->setCharacterStream(stream->getWriter());
rc = m_XalanTransformer.transform(*m_ParsedSource, pCompiledStylesheet, *m_resultTarget);
}
catch(...)
{
StringBuffer estr("[Exception running XSLT stylesheet]");
estr.appendf("[%s]", m_XalanTransformer.getLastError());
DBGLOG("%s", estr.str());
throw MakeStringException(2, "%s", estr.str());
}
if (rc < 0)
{
StringBuffer estr;
estr.appendf("[%s]", m_XalanTransformer.getLastError());
DBGLOG("%s", estr.str());
throw MakeStringException(2, "%s", estr.str());
}
return rc;
}
int CXslTransform::setXmlSource(const char *pszFileName)
{
if(m_ParsedSource != NULL)
{
m_XalanTransformer.destroyParsedSource(m_ParsedSource);
m_ParsedSource = NULL;
}
int theResult = 0;
try
{
std::ifstream theXMLStream(pszFileName);
const XSLTInputSource xmlinput(&theXMLStream);
theResult = m_XalanTransformer.parseSource(xmlinput, (const XalanParsedSource*&)m_ParsedSource);
}
catch(...)
{
m_ParsedSource = NULL;
StringBuffer estr("[Exception compiling xml]");
estr.appendf("[%s]", m_XalanTransformer.getLastError());
DBGLOG("%s", estr.str());
throw MakeStringException(2, "%s", estr.str());
}
if (!m_ParsedSource)
{
StringBuffer estr("[failed to compile xml]");
estr.appendf("[%s]", m_XalanTransformer.getLastError());
DBGLOG("%s", estr.str());
throw MakeStringException(2, "%s", estr.str());
}
return theResult;
}
int CXslTransform::setXmlSource(const char *pszBuffer, unsigned int nSize)
{
if(m_ParsedSource != NULL)
{
m_XalanTransformer.destroyParsedSource(m_ParsedSource);
m_ParsedSource = NULL;
}
int theResult = 0;
try
{
//std::istringstream theXMLStream(pszBuffer, nSize);
std::istringstream theXMLStream(pszBuffer);
const XSLTInputSource xmlinput(&theXMLStream);
theResult = m_XalanTransformer.parseSource(xmlinput, (const XalanParsedSource*&)m_ParsedSource);
}
catch(...)
{
m_ParsedSource = NULL;
StringBuffer estr("[Exception compiling xml]");
estr.appendf("[%s]", m_XalanTransformer.getLastError());
DBGLOG("%s", estr.str());
throw MakeStringException(2, "%s", estr.str());
}
if (!m_ParsedSource)
{
StringBuffer estr("[failed to compile xml]");
estr.appendf("[%s]", m_XalanTransformer.getLastError());
DBGLOG("%s", estr.str());
throw MakeStringException(2, "%s", estr.str());
}
return theResult;
}
int CXslTransform::loadXslFromFile(const char *pszFileName, const char *cacheId)
{
m_xslsource.setown(new CXslSource(pszFileName, m_sourceResolver?m_sourceResolver->getIncludeHandler():NULL, cacheId));
return 0;
}
int CXslTransform::setXslSource(const char *pszBuffer, unsigned int nSize, const char *cacheId, const char *rootpath)
{
assertex(cacheId && *cacheId);
m_xslsource.setown(new CXslSource(pszBuffer, nSize, m_sourceResolver?m_sourceResolver->getIncludeHandler():NULL, cacheId, rootpath));
return 0;
}
int CXslTransform::setXslNoCache(const char *pszBuffer, unsigned int nSize, const char *rootpath)
{
m_xslsource.setown(new CXslSource(pszBuffer, nSize, m_sourceResolver?m_sourceResolver->getIncludeHandler():NULL, NULL, rootpath));
return 0;
}
int CXslTransform::setResultTarget(const char *pszFileName)
{
closeResultTarget();
try
{
m_resultTargetFile.clear().append(pszFileName);
m_resultTarget = new XSLTResultTarget(XalanDOMString(pszFileName));
}
catch(...)
{
throw MakeStringException(1, "Exception opening file %s", pszFileName);
}
return 0;
}
int CXslTransform::setResultTarget(char *pszBuffer, unsigned int nSize)
{
closeResultTarget();
// Our output target that uses an ostrstream that will use the buffer
try
{
//m_ostrstream = new std::ostringstream(pszBuffer, nSize);
m_ostrstream = new std::ostringstream(pszBuffer);
m_resultTarget = new XSLTResultTarget(m_ostrstream);
}
catch(...)
{
throw MakeStringException(1, "Exception in setting character buffer as XSLT result target.");
}
return 0;
}
int CXslTransform::closeResultTarget()
{
if (m_resultTarget)
{
delete m_resultTarget;
m_resultTarget = 0;
}
if(m_ostrstream)
{
delete m_ostrstream;
m_ostrstream = 0;
}
return 0;
}
int CXslTransform::setParameter(const char *pszName, const char *pszExpression)
{
m_XalanTransformer.setStylesheetParam(XalanDOMString(pszName), XalanDOMString(pszExpression));
return 0;
}
int CXslTransform::setStringParameter(const char *pszName, const char *pszString)
{
m_XalanTransformer.setStylesheetParam(XalanDOMString(pszName), XalanDOMString(StringBuffer("'").append(pszString).append("'").str()));
return 0;
}
int CXslTransform::setIncludeHandler(IIncludeHandler* handler)
{
if(handler == NULL)
{
throw MakeStringException(-1, "From CXslTransform::setIncludeHandler: a NULL handler is passed in");
}
if(m_sourceResolver == NULL)
{
m_sourceResolver = new MemSourceResolver();
}
m_sourceResolver->setHandler(handler);
m_XalanTransformer.setEntityResolver(m_sourceResolver);
if(m_xslsource.get() != NULL)
{
m_xslsource->setIncludeHandler(handler);
}
return 0;
}
int CXslTransform::setExternalFunction(const char* pszNameSpace, IXslFunction* pXslFunction, bool set)
{
CXslFunction* pFn = (CXslFunction*) pXslFunction;
if (pFn == NULL || pFn->get() == NULL)
throw MakeStringException(-1, "Null pointer violation in CXslTransform::setExternalFunction.");
XalanDOMString nameSpace(pszNameSpace);
XalanDOMString functionName(pFn->getName());
bool bAssigned = pFn->isAssigned();
if (set && !bAssigned)
m_XalanTransformer.installExternalFunction(nameSpace, functionName, *pFn->get());
else
{
if (!set && bAssigned)
m_XalanTransformer.uninstallExternalFunction(nameSpace, functionName);
else
throw MakeStringException(-1, "XSLT external function assignment error!");
}
pFn->setAssigned(set);
return 0;
}
/*static*/
void CXslTransform::message(StringBuffer& out, const char* in, IXslTransform* pTransform)
{
CXslTransform* pTrans = dynamic_cast(pTransform);
pTrans->m_sMessages.append(in).append('\n');
}