blob: e4bcd8773d3b926917c773f6f6dd2aafc5fe6e90 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Class header file...
#include "XalanOutputStream.hpp"
#include <xercesc/util/TransService.hpp>
#include "xalanc/Include/STLHelper.hpp"
#include "XalanMessageLoader.hpp"
#include "XalanTranscodingServices.hpp"
namespace XALAN_CPP_NAMESPACE {
const XalanDOMChar XalanOutputStream::s_nlString[] =
{
XalanUnicode::charLF,
0
};
const XalanDOMChar XalanOutputStream::s_nlCRString[] =
{
XalanUnicode::charCR,
XalanUnicode::charLF,
0
};
const XalanDOMString::size_type XalanOutputStream::s_nlStringLength =
sizeof(s_nlString) / sizeof(s_nlString[0]) - 1;
const XalanDOMString::size_type XalanOutputStream::s_nlCRStringLength =
sizeof(s_nlCRString) / sizeof(s_nlCRString[0]) - 1;
XalanOutputStream::XalanOutputStream(
MemoryManager& theManager,
size_type theBufferSize,
size_type theTranscoderBlockSize,
bool fThrowTranscodeException) :
m_transcoderBlockSize(theTranscoderBlockSize),
m_transcoder(0),
m_bufferSize(theBufferSize),
m_buffer(theManager),
m_encoding(theManager),
m_writeAsUTF16(false),
m_throwTranscodeException(fThrowTranscodeException),
m_transcodingBuffer(theManager)
{
if (m_bufferSize == 0)
{
m_bufferSize = 1;
}
m_buffer.reserve(theBufferSize + 1);
}
XalanOutputStream::~XalanOutputStream()
{
XalanTranscodingServices::destroyTranscoder(m_transcoder);
}
void
XalanOutputStream::write(
const XalanDOMChar* theBuffer,
size_type theBufferLength)
{
assert(theBuffer != 0);
if (theBufferLength + m_buffer.size() > m_bufferSize)
{
flushBuffer();
}
if (theBufferLength > m_bufferSize)
{
assert(m_buffer.empty() == true);
doWrite(theBuffer, theBufferLength);
}
else
{
m_buffer.insert(m_buffer.end(),
theBuffer,
theBuffer + theBufferLength);
}
}
void
XalanOutputStream::transcode(
const XalanDOMChar* theBuffer,
size_type theBufferLength,
TranscodeVectorType& theDestination)
{
if (m_transcoder == 0)
{
if (TranscodeToLocalCodePage(
theBuffer,
theBufferLength,
theDestination) == false)
{
if (m_throwTranscodeException == true)
{
XalanDOMString theExceptionBuffer(theDestination.getMemoryManager());
throw TranscodingException(
theExceptionBuffer,
0);
}
}
}
else
{
bool fDone = false;
// Keep track of the total bytes we've added to the
// destination vector, and the total bytes we've
// eaten from theBuffer.
size_type theTotalBytesFilled = 0;
size_type theTotalBytesEaten = 0;
// Keep track of the current position in the input buffer,
// and amount remaining in the buffer, since we may not be
// able to transcode it all at once.
const XalanDOMChar* theBufferPosition = theBuffer;
size_type theRemainingBufferLength = theBufferLength;
// Keep track of the destination size, and the target size, which is
// the size of the destination that has not yet been filled with
// transcoded characters. Double the buffer size, in case we're
// transcoding to a 16-bit encoding.
// $$$ ToDo: We need to know the size of an encoding, so we can
// do the right thing with the destination size.
size_type theDestinationSize = theBufferLength * 2;
size_type theTargetSize = theDestinationSize;
do
{
// Resize the buffer...
theDestination.resize(theDestinationSize + 1);
size_type theSourceBytesEaten = 0;
size_type theTargetBytesEaten = 0;
XalanTranscodingServices::eCode theResult =
m_transcoder->transcode(
theBufferPosition,
theRemainingBufferLength,
reinterpret_cast<XMLByte*>(&theDestination[0]) + theTotalBytesFilled,
theTargetSize,
theSourceBytesEaten,
theTargetBytesEaten);
if(theResult != XalanTranscodingServices::OK)
{
if (m_throwTranscodeException == true)
{
XalanDOMString theExceptionBuffer(theDestination.getMemoryManager());
throw TranscodingException(
theExceptionBuffer,
0);
}
}
theTotalBytesFilled += theTargetBytesEaten;
theTotalBytesEaten += theSourceBytesEaten;
if (theTotalBytesEaten == theBufferLength)
{
fDone = true;
}
else
{
assert(theTotalBytesEaten < theBufferLength);
// Update everything...
theBufferPosition += theSourceBytesEaten;
theRemainingBufferLength -= theSourceBytesEaten;
// The new target size will always be the
// current destination size, since we
// grow by a factor of 2. This will
// need to change if the factor is
// every changed.
theTargetSize = theDestinationSize;
// Grow the destination by a factor of
// two 2. See the previous comment if
// you want to change this.
theDestinationSize = theDestinationSize * 2;
}
} while(fDone == false);
// Resize things, if there are any extra bytes...
if (theDestination.size() != theTotalBytesFilled)
{
theDestination.resize(theTotalBytesFilled);
}
}
}
void
XalanOutputStream::setOutputEncoding(const XalanDOMString& theEncoding)
{
// Flush, just in case. This should probably be an error...
flushBuffer();
XalanTranscodingServices::destroyTranscoder(m_transcoder);
m_transcoder = 0;
XalanTranscodingServices::eCode theCode = XalanTranscodingServices::OK;
if (XalanTranscodingServices::encodingIsUTF16(theEncoding) == true)
{
m_writeAsUTF16 = true;
}
else
{
m_transcoder = XalanTranscodingServices::makeNewTranscoder(
getMemoryManager(),
theEncoding,
theCode,
m_transcoderBlockSize);
if (theCode == XalanTranscodingServices::UnsupportedEncoding ||
theCode == XalanTranscodingServices::SupportFilesNotFound)
{
XalanDOMString theBuffer(getMemoryManager());
throw UnsupportedEncodingException(theEncoding, theBuffer, 0);
}
else if (theCode != XalanTranscodingServices::OK)
{
XalanDOMString theBuffer(getMemoryManager());
throw TranscoderInternalFailureException(theEncoding, theBuffer, 0);
}
assert(m_transcoder != 0);
}
m_encoding = theEncoding;
const XalanTranscodingServices::XalanXMLByte* theProlog =
XalanTranscodingServices::getStreamProlog(theEncoding);
assert(theProlog != 0);
const size_type theLength = XalanTranscodingServices::length(theProlog);
if (theLength > 0)
{
write(reinterpret_cast<const char*>(theProlog), theLength);
}
}
bool
XalanOutputStream::canTranscodeTo(XalanUnicodeChar theChar) const
{
if (m_transcoder != 0)
{
return m_transcoder->canTranscodeTo(theChar);
}
else
{
// We'll always return true here, since an exception will be
// thrown when we try to transcode. We'ed like to enable the
// commented out line, if we can ever figure out how to see
// if a character can be encoded.
return true;
}
}
void
XalanOutputStream::flushBuffer()
{
if (m_buffer.empty() == false)
{
CollectionClearGuard<BufferType> theGuard(m_buffer);
assert(size_type(m_buffer.size()) == m_buffer.size());
doWrite(&*m_buffer.begin(), size_type(m_buffer.size()));
}
assert(m_buffer.empty() == true);
}
void
XalanOutputStream::doWrite(
const XalanDOMChar* theBuffer,
size_type theBufferLength)
{
assert(theBuffer != 0);
if (m_writeAsUTF16 == true)
{
// This is a hack to write UTF-16 through as if it
// were just chars. Saves lots of time "transcoding."
writeData(reinterpret_cast<const char*>(theBuffer), theBufferLength * 2);
}
else
{
transcode(theBuffer, theBufferLength, m_transcodingBuffer);
assert(&m_transcodingBuffer[0] != 0);
assert(size_type(m_transcodingBuffer.size()) == m_transcodingBuffer.size());
writeData(
&m_transcodingBuffer[0],
size_type(m_transcodingBuffer.size()));
}
}
void
XalanOutputStream::setBufferSize(size_type theBufferSize)
{
flushBuffer();
if (theBufferSize == 0)
{
m_bufferSize = 1;
}
else
{
m_bufferSize = theBufferSize;
}
if (m_buffer.size() < m_bufferSize)
{
// Enlarge the buffer...
m_buffer.reserve(theBufferSize + 1);
}
else if (m_buffer.size() > m_bufferSize)
{
// Shrink the buffer.
// Create a temp buffer and make it
// the correct size.
BufferType temp(getMemoryManager());
temp.reserve(theBufferSize + 1);
// Swap temp with m_buffer so that
// m_buffer is now the correct size.
temp.swap(m_buffer);
}
}
void
XalanOutputStream::newline()
{
// We've had requests to make this a run-time switch, but for now,
// it's compile time only...
#if defined(XALAN_NEWLINE_IS_CRLF)
write(s_nlCRString, s_nlCRStringLength);
#else
write(s_nlString, s_nlStringLength);
#endif
}
const XalanDOMChar*
XalanOutputStream::getNewlineString() const
{
// We've had requests to make this a run-time switch, but for now,
// it's compile time only...
#if defined(XALAN_NEWLINE_IS_CRLF)
return s_nlCRString;
#else
return s_nlString;
#endif
}
XalanOutputStream::XalanOutputStreamException::XalanOutputStreamException(
const XalanDOMString& theMessage,
MemoryManager& theManager,
const Locator* theLocator) :
XSLException(
theMessage,
theManager,
theLocator)
{
}
XalanOutputStream::XalanOutputStreamException::XalanOutputStreamException(
const XalanDOMString& theMessage,
MemoryManager& theManager) :
XSLException(
theMessage,
theManager)
{
}
XalanOutputStream::XalanOutputStreamException::XalanOutputStreamException(const XalanOutputStreamException& other):
XSLException(other)
{
}
XalanOutputStream::XalanOutputStreamException::~XalanOutputStreamException()
{
}
const XalanDOMChar*
XalanOutputStream::XalanOutputStreamException::getType() const
{
static const XalanDOMChar s_type[] =
{
XalanUnicode::charLetter_X,
XalanUnicode::charLetter_a,
XalanUnicode::charLetter_l,
XalanUnicode::charLetter_a,
XalanUnicode::charLetter_n,
XalanUnicode::charLetter_F,
XalanUnicode::charLetter_i,
XalanUnicode::charLetter_l,
XalanUnicode::charLetter_e,
XalanUnicode::charLetter_O,
XalanUnicode::charLetter_u,
XalanUnicode::charLetter_t,
XalanUnicode::charLetter_p,
XalanUnicode::charLetter_u,
XalanUnicode::charLetter_t,
XalanUnicode::charLetter_S,
XalanUnicode::charLetter_t,
XalanUnicode::charLetter_r,
XalanUnicode::charLetter_e,
XalanUnicode::charLetter_a,
XalanUnicode::charLetter_m,
XalanUnicode::charLetter_E,
XalanUnicode::charLetter_x,
XalanUnicode::charLetter_c,
XalanUnicode::charLetter_e,
XalanUnicode::charLetter_p,
XalanUnicode::charLetter_t,
XalanUnicode::charLetter_i,
XalanUnicode::charLetter_o,
XalanUnicode::charLetter_n,
0
};
return s_type;
}
const XalanDOMChar*
XalanOutputStream::UnsupportedEncodingException::getType() const
{
static const XalanDOMChar s_type[] =
{
XalanUnicode::charLetter_U,
XalanUnicode::charLetter_n,
XalanUnicode::charLetter_s,
XalanUnicode::charLetter_u,
XalanUnicode::charLetter_p,
XalanUnicode::charLetter_p,
XalanUnicode::charLetter_o,
XalanUnicode::charLetter_r,
XalanUnicode::charLetter_t,
XalanUnicode::charLetter_e,
XalanUnicode::charLetter_d,
XalanUnicode::charLetter_E,
XalanUnicode::charLetter_n,
XalanUnicode::charLetter_c,
XalanUnicode::charLetter_o,
XalanUnicode::charLetter_d,
XalanUnicode::charLetter_i,
XalanUnicode::charLetter_n,
XalanUnicode::charLetter_g,
XalanUnicode::charLetter_E,
XalanUnicode::charLetter_x,
XalanUnicode::charLetter_c,
XalanUnicode::charLetter_e,
XalanUnicode::charLetter_p,
XalanUnicode::charLetter_t,
XalanUnicode::charLetter_i,
XalanUnicode::charLetter_o,
XalanUnicode::charLetter_n,
0
};
return s_type;
}
XalanOutputStream::UnsupportedEncodingException::UnsupportedEncodingException(
const XalanDOMString& theEncoding,
XalanDOMString& theBuffer,
const Locator* theLocator) :
XalanOutputStreamException(
XalanMessageLoader::getMessage(
theBuffer,
XalanMessages::UnsupportedEncoding_1Param,
theEncoding),
theBuffer.getMemoryManager(),
theLocator),
m_encoding(
theEncoding,
theBuffer.getMemoryManager())
{
}
XalanOutputStream::UnsupportedEncodingException::UnsupportedEncodingException(
const XalanDOMString& theEncoding,
XalanDOMString& theBuffer) :
XalanOutputStreamException(
XalanMessageLoader::getMessage(
theBuffer,
XalanMessages::UnsupportedEncoding_1Param,
theEncoding),
theBuffer.getMemoryManager()),
m_encoding(
theEncoding,
theBuffer.getMemoryManager())
{
}
XalanOutputStream::UnsupportedEncodingException::~UnsupportedEncodingException()
{
}
XalanOutputStream::TranscoderInternalFailureException::TranscoderInternalFailureException(
const XalanDOMString& theEncoding,
XalanDOMString& theBuffer,
const Locator* theLocator) :
XalanOutputStreamException(
XalanMessageLoader::getMessage(
theBuffer,
XalanMessages::UnknownErrorOccurredWhileTranscodingToEncoding_1Param,
theEncoding),
theBuffer.getMemoryManager(),
theLocator),
m_encoding(
theEncoding,
theBuffer.getMemoryManager())
{
}
XalanOutputStream::TranscoderInternalFailureException::TranscoderInternalFailureException(const TranscoderInternalFailureException& other) :
XalanOutputStreamException(other),
m_encoding(
other.m_encoding,
other.m_memoryManager)
{
}
XalanOutputStream::TranscoderInternalFailureException::~TranscoderInternalFailureException()
{
}
const XalanDOMChar*
XalanOutputStream::TranscoderInternalFailureException::getType() const
{
static const XalanDOMChar s_type[] =
{
XalanUnicode::charLetter_T,
XalanUnicode::charLetter_r,
XalanUnicode::charLetter_a,
XalanUnicode::charLetter_n,
XalanUnicode::charLetter_s,
XalanUnicode::charLetter_c,
XalanUnicode::charLetter_o,
XalanUnicode::charLetter_d,
XalanUnicode::charLetter_e,
XalanUnicode::charLetter_r,
XalanUnicode::charLetter_I,
XalanUnicode::charLetter_n,
XalanUnicode::charLetter_t,
XalanUnicode::charLetter_e,
XalanUnicode::charLetter_r,
XalanUnicode::charLetter_n,
XalanUnicode::charLetter_a,
XalanUnicode::charLetter_l,
XalanUnicode::charLetter_F,
XalanUnicode::charLetter_a,
XalanUnicode::charLetter_i,
XalanUnicode::charLetter_l,
XalanUnicode::charLetter_u,
XalanUnicode::charLetter_r,
XalanUnicode::charLetter_e,
XalanUnicode::charLetter_E,
XalanUnicode::charLetter_x,
XalanUnicode::charLetter_c,
XalanUnicode::charLetter_e,
XalanUnicode::charLetter_p,
XalanUnicode::charLetter_t,
XalanUnicode::charLetter_i,
XalanUnicode::charLetter_o,
XalanUnicode::charLetter_n,
0
};
return s_type;
}
XalanOutputStream::TranscodingException::TranscodingException(
XalanDOMString& theBuffer,
const Locator* theLocator) :
XalanOutputStreamException(
XalanMessageLoader::getMessage(
theBuffer,
XalanMessages::AnErrorOccurredWhileTranscoding),
theBuffer.getMemoryManager(),
theLocator)
{
}
XalanOutputStream::TranscodingException::TranscodingException(const TranscodingException& other) :
XalanOutputStreamException(other)
{
}
XalanOutputStream::TranscodingException::~TranscodingException()
{
}
const XalanDOMChar*
XalanOutputStream::TranscodingException::getType() const
{
static const XalanDOMChar s_type[] =
{
XalanUnicode::charLetter_T,
XalanUnicode::charLetter_r,
XalanUnicode::charLetter_a,
XalanUnicode::charLetter_n,
XalanUnicode::charLetter_s,
XalanUnicode::charLetter_c,
XalanUnicode::charLetter_o,
XalanUnicode::charLetter_d,
XalanUnicode::charLetter_i,
XalanUnicode::charLetter_n,
XalanUnicode::charLetter_g,
XalanUnicode::charLetter_E,
XalanUnicode::charLetter_x,
XalanUnicode::charLetter_c,
XalanUnicode::charLetter_e,
XalanUnicode::charLetter_p,
XalanUnicode::charLetter_t,
XalanUnicode::charLetter_i,
XalanUnicode::charLetter_o,
XalanUnicode::charLetter_n,
0
};
return s_type;
}
XalanDOMString&
XalanOutputStream::formatMessage(
const XalanDOMString& theMessage,
int theErrorCode,
XalanDOMString& theBuffer)
{
XalanDOMString strErrorCode(theBuffer.getMemoryManager());
XalanDOMString strErrorMsg(theBuffer.getMemoryManager());
NumberToDOMString(theErrorCode, strErrorCode);
theBuffer.assign(theMessage);
XalanMessageLoader::getMessage(
strErrorMsg,
XalanMessages::SystemErrorCode_1Param,
strErrorCode);
theBuffer.append(strErrorMsg);
return theBuffer;
}
}