📄 simplexml.inl
字号:
/*
* ============================================================================
* Name : CSimpleXml from SimpleXml.cpp
* Created : 27/11/2003 by EMCC Software Ltd
* Description: Parsing and generation of a simple subset of XML
* Version : 1
* Copyright: EMCC Software Ltd
* ============================================================================
*/
#include "SimpleXml.h"
// Debugging TRACE macro
#include <e32svr.h>
#ifdef _DEBUG
#define TRACE RDebug::Print
#else
#define TRACE if (0) RDebug::Print
#endif
// Literal strings are accessed through templated functions generated by the
// following macro. This allows ASCII or Unicode literals to be generated as
// appropriate, whilst still only defining the string in one place and generating
// one copy of each string in memory (or two copies if both Unicode and Ascii
// parsers are used within the same app)
#define XML_DEFINE_STRING(name, string) \
void Str##name(const TDesC8*& aT) /* ASCII specialization */ \
{ \
_LIT8(str, string); \
aT = &str; \
} \
void Str##name(const TDesC16*& aT) /* Unicode specialization */ \
{ \
_LIT16(str, string); \
aT = &str; \
} \
static const TInt name##_Len = sizeof(string) - 1;
#define XML_USE_STRING(name) \
const TXmlDesC *name##_Ptr; \
Str##name(name##_Ptr);
XML_DEFINE_STRING(XmlDeclaration, "<?xml version=\"1.0\"?>")
XML_DEFINE_STRING(XmlDeclarationMarker, "<?xml")
XML_DEFINE_STRING(XmlDeclarationEnd, "?>")
XML_DEFINE_STRING(XmlEntityAmp, "&")
XML_DEFINE_STRING(XmlEntityLt, "<")
XML_DEFINE_STRING(XmlEntityGt, ">")
XML_DEFINE_STRING(XmlEntityApos, "'")
XML_DEFINE_STRING(XmlEntityQuot, """)
const TInt KOutputBufferGranularity = 256;
/*
* ============================================================================
* CSimpleXml class implementation
* ============================================================================
*/
/**
* Two-phase construction
*/
CSimpleXml* CSimpleXml::NewL()
{
CSimpleXml* self = NewLC();
CleanupStack::Pop(self);
return self;
}
CSimpleXml* CSimpleXml::NewLC()
{
CSimpleXml* self = new (ELeave) CSimpleXml();
CleanupStack::PushL(self);
self->ConstructL();
return self;
}
/**
* First-phase constructor
*/
CSimpleXml::CSimpleXml()
{
iMaxNesting = KDefaultMaxXmlElementNesting;
}
/**
* Second-phase constructor
*/
void CSimpleXml::ConstructL()
{
TRACE(_L("CSimpleXml::ConstructL\n"));
}
/**
* Destructor
*/
CSimpleXml::~CSimpleXml()
{
Reset();
}
/**
* Parse
* Generates an internal representation of the input XML source
*/
void CSimpleXml::ParseL(const TXmlDesC &aXml)
{
// Set member variables to reference the input XML
iAllXml.Set(aXml);
iCurXml.Set(aXml);
// Clear out any existing XML internal representation
delete iRootElement;
iRootElement = NULL;
// Process the XML declaration which should appear at the top of the XML
if (FindNextChunkL() == EChunkXmlDeclaration)
{
GetXmlDeclarationL();
}
// Now process the root element (and all contents thereof, i.e. everything)
if (FindNextChunkL() != EChunkXmlElement)
{
User::Leave(/*KErrArgument*/KErrNoMemory);
}
iRootElement = new (ELeave) CXmlElement();
iNesting = -1; // root element will be at nesting level 0
GetXmlElementL(*iRootElement);
__ASSERT_DEBUG(iNesting == -1, Panic(EPanicBadNesting)); // shouldn't be possible
}
/**
* RootElement
* Accessor for the root element
*/
CXmlElement& CSimpleXml::RootElement() const
{
if (!iRootElement) Panic(EPanicNoRootElement);
return *iRootElement;
}
/**
* Reset
* Clear out the internal XML representation
*/
void CSimpleXml::Reset()
{
delete iRootElement;
iRootElement = NULL;
delete iTempBuf;
iTempBuf = NULL;
iXmlAttributes.ResetAndDestroy();
}
/**
* CreateRootElementL
* Create a new root element (replaces any existing data)
*/
CXmlElement& CSimpleXml::CreateRootElementL(const TXmlDesC &aName)
{
Reset();
iRootElement = new (ELeave) CXmlElement();
iRootElement->iName = aName.AllocL();
return *iRootElement;
}
/**
* Output our contents as XML text
*/
HXmlBufC *CSimpleXml::OutputLC()
{
HXmlBufC *output = OutputL();
CleanupStack::PushL(output);
return output;
}
HXmlBufC *CSimpleXml::OutputL()
{
// We will use a segmented array of text characters to buffer the output, so that the array
// class will manage the complexities of growing the temporary output buffer for us, and in a reasonably
// efficient fashion
delete iTempBuf;
iTempBuf = NULL;
iTempBuf = new (ELeave) CArrayFixSeg<TXmlText>(KOutputBufferGranularity);
// Now output the XML to the temporary array buffer
// Start with the XML declaration
XML_USE_STRING(XmlDeclaration);
OutputL(*XmlDeclaration_Ptr);
// Now output the root element (and all contents thereof, i.e. everything)
iNesting = -1; // root element will be at nesting level 0
OutputL(RootElement());
__ASSERT_DEBUG(iNesting == -1, Panic(EPanicBadNesting)); // shouldn't be possible
// Now copy the data over into an HXmlBufC for our caller.
// Note that the HXmlBufC is not pushed onto the cleanup stack: this is because we aren't
// calling anything that might leave.
TInt count = iTempBuf->Count();
HXmlBufC *outBuf = HXmlBufC::NewL(count); // N.B. not pushed onto cleanup stack
TXmlPtr outPtr = outBuf->Des();
// Copy the array data one segment at a time
for (TInt nIndex = 0; nIndex < count; nIndex += KOutputBufferGranularity)
{
TInt toMove = Min(KOutputBufferGranularity, count - nIndex);
TXmlText *seg = &iTempBuf->At(nIndex);
outPtr.Append(seg, toMove);
}
delete iTempBuf;
iTempBuf = NULL;
return outBuf;
}
/**
* FindNextChunkL
* Finds the start of the next chunk and determines its type
*/
CSimpleXml::TChunkType CSimpleXml::FindNextChunkL()
{
// find next non-white-space character
TChar c;
TInt i;
for (i = 0; (c = iCurXml[i]).IsSpace(); ++i)
;
if (c == '<')
{
iCurXml.Set(iCurXml.Mid(i)); // step the current pointer
c = iCurXml[1]; // pick up the character after '<'
return c == '?' ? EChunkXmlDeclaration
: c == '/' ? EChunkXmlElementEnd
: EChunkXmlElement;
}
else
{
return EChunkText;
}
}
/**
* GetXmlDeclarationL
*/
void CSimpleXml::GetXmlDeclarationL()
{
// Crude first implementation
XML_USE_STRING(XmlDeclarationMarker);
if (iCurXml.Left(XmlDeclarationMarker_Len) != *XmlDeclarationMarker_Ptr)
{
User::Leave(/*KErrArgument*/KErrNotSupported);
}
iCurXml.Set(iCurXml.Mid(XmlDeclarationMarker_Len));
// Read any attributes in the XML declaration
while (GetPossibleAttributeL(iXmlAttributes))
continue;
// (above call has stepped past any white space)
// Check that the XML declaration is terminated by "?>"
XML_USE_STRING(XmlDeclarationEnd);
if (iCurXml.Left(XmlDeclarationEnd_Len) != *XmlDeclarationEnd_Ptr)
{
User::Leave(/*KErrArgument*/KErrTotalLossOfPrecision);
}
iCurXml.Set(iCurXml.Mid(XmlDeclarationEnd_Len));
}
/**
* GetXmlElementL
*/
void CSimpleXml::GetXmlElementL(CXmlElement &aElement)
{
// Protect against stack overflow by limiting the depth to which elements
// can be nested, since we will be parsing recursively
if (++iNesting > iMaxNesting)
{
User::Leave(KErrArgument);
}
iCurXml.Set(iCurXml.Mid(1)); // step past '<'
GetNameL(aElement.iName);
while (GetPossibleAttributeL(aElement.iAttributes))
continue;
// (above call has stepped past any white space)
if (iCurXml[0] == '/')
{
// (no space allowed here)
if (iCurXml[1] != '>')
{
User::Leave(KErrArgument);
}
iCurXml.Set(iCurXml.Mid(2)); // step past "/>"
--iNesting; // reduce nesting level
return; // empty element processing finished
}
if (iCurXml[0] != '>')
{
User::Leave(KErrArgument);
}
iCurXml.Set(iCurXml.Mid(1)); // step past ">"
TChunkType type;
do
{
switch (type = FindNextChunkL())
{
case EChunkXmlElement:
{
CXmlElement& newElement = aElement.AddElementL();
GetXmlElementL(newElement);
break;
}
case EChunkText:
{
CXmlText& newText = aElement.AddTextL();
GetTextL(newText.iText);
break;
}
case EChunkXmlElementEnd:
{
// Step to the character after "</" which should be the start of the name
iCurXml.Set(iCurXml.Mid(2));
HXmlBufC *endName;
GetNameL(endName);
TBool same = *endName == *aElement.iName;
delete endName;
if (!same) User::Leave(KErrArgument);
if (iCurXml[0] != '>') User::Leave(KErrArgument);
iCurXml.Set(iCurXml.Mid(1)); // step past terminating '>'
break;
}
default:
User::Leave(KErrArgument);
}
} while (type != EChunkXmlElementEnd);
--iNesting; // reduce nesting level
}
/**
* GetNameL
*/
void CSimpleXml::GetNameL(HXmlBufC*& aName)
{
// check the first character is a letter or underscore
TChar c = iCurXml[0];
if (!c.IsAlpha() && c != '_')
{
User::Leave(/*KErrArgument*/KErrBadHandle);
}
// find first non-name character after this (XML names can
// contain letters, digits, hyphens, full-stops and underlines
TInt i;
for (i = 1; ; ++i)
{
c = iCurXml[i];
if (!c.IsAlphaDigit() && c != '-' && c != '.' && c != '_')
break; // end of name found
}
// set up the name in allocated memory for caller (who takes responsibility
// for it - but we must not leave once we have allocated the memory...)
aName = iCurXml.Left(i).AllocL();
iCurXml.Set(iCurXml.Mid(i)); // step the current pointer past the name
}
/**
* GetPossibleAttributeL
*/
TBool CSimpleXml::GetPossibleAttributeL(RPointerArray<CXmlAttribute >& aAttributeList)
{
// step past any white space
TInt i;
TChar c;
for (i = 0; (c = iCurXml[i]).IsSpace(); ++i)
;
if (i == 0) return FALSE; // no whitespace separator means no attribute found
iCurXml.Set(iCurXml.Mid(i)); // step the current pointer past the white space
if (!c.IsAlpha() && c != '_') return FALSE; // not a valid attribute name start
// From here on, we know that the data ought to be an attribute and we will Leave
// if the format is wrong
// Create a new attribute object and add it to the array of attributes for this element
CXmlAttribute &attrib = CXmlAttribute::AddAttributeToListL(aAttributeList);
// Fill in the attribute name keyword
GetNameL(attrib.iKeyword);
// Step past any (optional) white space
for (i = 0; (c = iCurXml[i]).IsSpace(); ++i)
;
// Ensure "equals" sign separator is present
if (c != '=') User::Leave(/*KErrArgument*/KErrUnderflow);
// Step past any (optional) white space
TChar quote;
for (++i; (quote = iCurXml[i]).IsSpace(); ++i)
;
// Validate the starting quotation mark for the attribute value
if (quote != TChar('"') && quote != TChar('\'')) User::Leave(KErrArgument);
iCurXml.Set(iCurXml.Mid(i + 1)); // step the current pointer to the start of the value
GetTextL(attrib.iValue, quote); // get text, with entity resolution, till end quote found
if (iCurXml[0] != (TXmlText) quote) User::Leave(/*KErrArgument*/KErrAlreadyExists);
iCurXml.Set(iCurXml.Mid(1)); // step the current pointer past the end quote
return ETrue;
}
/**
* GetTextL
*/
void CSimpleXml::GetTextL(HXmlBufC*& aText, TChar aQuote /* = 0 */)
{
TInt pass;
TXmlPtr output(NULL, 0);
XML_USE_STRING(XmlEntityAmp);
XML_USE_STRING(XmlEntityLt);
XML_USE_STRING(XmlEntityGt);
XML_USE_STRING(XmlEntityApos);
XML_USE_STRING(XmlEntityQuot);
// Use two passes through the data - first pass calculates the length only; second
// pass outputs the converted data. This saves having to re-allocate buffers as the data
// grows.
for (pass = 1; pass <= 2; ++pass)
{
TInt i = 0;
TChar c;
// Not strictly kosher XML behaviour, but we will strip out all leading and trailing white
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -