blob: c05ed820c3bfbd64fd5564038300fbd0e1bbb8df [file] [log] [blame]
// **********************************************************************
// @@@ START COPYRIGHT @@@
//
// 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.
//
// @@@ END COPYRIGHT @@@
// **********************************************************************
#include <ctype.h>
#include "XMLUtil.h"
#include <ctype.h>
//
// XMLDocument
//
XMLDocument::XMLDocument(NAHeap *heap, XMLElementMapper &elementMapper)
: documentElementMapper_(elementMapper),
documentElement_(NULL),
currElemPtr_(NULL),
parser(XML_ParserCreate(NULL))
{
XML_UseParserAsHandlerArg(parser);
XML_SetElementHandler(parser, startElementHandler, endElementHandler);
XML_SetCharacterDataHandler(parser, charDataHandler);
// The heap used while parsing the XML document is accessed through the
// XML_GetNAHeap/XML_SetNAHeap functions we added to expat.
XML_SetNAHeap(parser, heap);
// Put ptr to XMLDocument in parser's user data. If a shared pointer is used
// for the XMLDocument this is still ok, because when the reference count
// for the shared pointer gets to 0 and the XMLDocument gets deleted, the
// then-dangling conventional pointer stored in the parser data structure will
// not be accessible because the parser is private to XMLDocument.
XML_SetUserData(parser, this);
}
XMLElementPtr XMLDocument::parse(const char *buf, Int32 len, Int32 done)
{
if (documentElement_)
throw XMLException("Already contains a document; use clear() first");
if (!XML_Parse(parser, buf, len, done))
throw XMLException("%s at column %d of line %d",
XML_ErrorString(XML_GetErrorCode(parser)),
XML_GetCurrentColumnNumber(parser),
XML_GetCurrentLineNumber(parser));
if (done)
documentElement_ = getCurrentElement(parser);
return documentElement_;
}
void XMLDocument::serialize(XMLString &xml)
{
if (documentElement_)
documentElement_->toXML(xml);
else
throw XMLException("This XMLDocument does not currently contain a document");
}
const XMLElementPtr& XMLDocument::getDocumentElement() const
{
return documentElement_;
}
void XMLDocument::startElementHandler(void *parser,
const char *elemName,
const char **atts)
{
XMLElementPtr containingElement = getCurrentElement(parser);
if (containingElement)
containingElement->startElement(parser, elemName, atts);
else
{
containingElement = ((XMLDocument*)(XML_GetUserData(parser)))->documentElementMapper_(parser, (char*)elemName, atts);
if (!containingElement)
throw XMLException("Unknown top-level element: %s", elemName);
else
setCurrentElement(parser, containingElement);
}
}
void XMLDocument::endElementHandler(void *parser, const char *name)
{
getCurrentElement(parser)->endElement(parser, name);
// setCurrentElement(parser, getCurrentElement(parser)->getParent());
}
void XMLDocument::charDataHandler(void *parser, const char *data, Int32 len)
{
// @ZX
XMLElementPtr ptr = getCurrentElement(parser);
ptr->charData(parser, data, len);
//getCurrentElement(parser)->charData(parser, data, len);
}
//
// XMLElement
//
void XMLElement::toXML(XMLString& xml, NABoolean sameLine, NABoolean noEndTag)
{
// Indent to the current level and write start tag and attributes.
xml.indent();
xml.append('<').append(getElementName()).append(' ');
serializeAttrs(xml);
if (xml[xml.length()-1] == ' ')
xml.remove(xml.length()-1);
// If caller requests abbreviated form for element with no content, make sure
// the element is actually empty. Note the early return if the abbreviated
// form is used.
if (noEndTag)
{
size_t len = xml.length();
serializeBody(xml);
assertLogAndThrow(CAT_SQL_COMP_QR_COMMON, LL_MVQR_FAIL,
len == xml.length(), QRLogicException,
"Tried to omit closing tag, but element has content");
xml.append("/>");
xml.endLine();
return;
}
else
xml.append('>');
// If content is to appear on same line, serialize the body without any
// newlines or indentation.
if (sameLine)
serializeBody(xml);
else
{
xml.endLine();
xml.incrementLevel();
serializeBody(xml);
xml.decrementLevel();
xml.indent();
}
// Write the end tag and end the line.
xml.append("</").append(getElementName()).append('>');
xml.endLine();
}
void XMLElement::stripWhitespace(const char*& data, Int32& len)
{
const char* endPtr = data + len - 1;
while (data <= endPtr && isspace(*data))
data++;
while (data <= endPtr && isspace(*endPtr))
endPtr--;
len = (endPtr - data) + 1;
}
NABoolean XMLElement::isspace(char c)
{
switch (c)
{
case ' ':
case '\t':
case '\r':
case '\n':
return TRUE;
default:
return FALSE;
}
}
//
// XMLBitmap
//
char XMLBitmap::HexChars_[] =
{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
void XMLBitmap::toXML(XMLString& xml) const
{
Int32 nWords = getWordSize();
Int32 nBytes = nWords * sizeof(WordAsBits);
NAString hexString((NASize_T)(nBytes*2));
for (Int32 i=0; i<nWords; i++)
{
// Get the word.
WordAsBits thisWord = word(i);
for (Int32 j=0; j<sizeof(WordAsBits)*2; j++)
{
// Get the half byte.
Int32 offset = j*4;
WordAsBits mask = 0x000F << offset;
unsigned char thisHalfByte = (unsigned char)((thisWord & mask) >> offset);
char thisChar = HexChars_[thisHalfByte];
hexString += thisChar;
}
}
xml.append(hexString);
} // toXML()
void XMLBitmap::initFromHexString(const char* hexChar, UInt32 nBits)
{
resize((nBits + BitsPerWord) / BitsPerWord);
Int32 nWords = getWordSize();
CollIndex wordStartBit;
// The outer loop is on 32 bit words.
for (Int32 wordIndex=0; wordIndex<nWords; wordIndex++)
{
wordStartBit = wordIndex * BitsPerWord;
// Next loop is on 4 bit nibbles (each is 1 hex digit)
// Note that the index of this loop must be a signed int, because it goes
// negative for the termination condition.
for (Int32 nibbleBitOffsetInWord = (sizeof(WordAsBits)*2-1)*4;
nibbleBitOffsetInWord >= 0;
nibbleBitOffsetInWord -= 4)
{
char thisDigit = *(hexChar++);
if (thisDigit == '0')
continue;
// Finally loop on the bits within the nibble.
unsigned char thisBits = hexDigitToInt(thisDigit);
for (Int32 bitInNibble=0; bitInNibble<4; bitInNibble++)
{
if ((thisBits >> (3 - bitInNibble)) & BIT0)
addElementFast(wordStartBit + nibbleBitOffsetInWord + bitInNibble);
}
}
}
} // initFromHexString()
unsigned char XMLBitmap::hexDigitToInt(char c)
{
assertLogAndThrow1(CAT_SQL_COMP_QR_COMMON, LL_MVQR_FAIL,
(c>='0' && c<='9') || (c>='A' && c<='F'), QRLogicException,
"input to hexDigitToInt() must be a hex digit, not '%c'.", c);
if (c <= '9')
return c-'0';
else
return 10 + (c-'A');
}