diff -r 8e7494275d3a -r 4f0867e42d62 omads/omadsextensions/dsutils/nsmlfolderutils/src/nsmlxmlparser.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/omads/omadsextensions/dsutils/nsmlfolderutils/src/nsmlxmlparser.cpp Wed Sep 01 12:30:02 2010 +0100 @@ -0,0 +1,798 @@ +/* +* Copyright (c) 2004 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Sources +* +*/ + + +// 1.2 Changes: nsmlxmlparser module added + +// ------------------------------------------------------------------------------------------------ +// Includes +// ------------------------------------------------------------------------------------------------ +#include +#include +#include +#include "nsmlparserconstants.h" + + +// ------------------------------------------------------------------------------------------------ +// +// CNSmlXmlParser methods +// +// ------------------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------------------ +// Constructor +// ------------------------------------------------------------------------------------------------ +EXPORT_C CNSmlXmlParser::CNSmlXmlParser() + { + } + + +// ------------------------------------------------------------------------------------------------ +// Destructor. +// ------------------------------------------------------------------------------------------------ +EXPORT_C CNSmlXmlParser::~CNSmlXmlParser() + { + if( iBuffer ) delete iBuffer; + if ( iCompleteBuffer ) delete iCompleteBuffer; + } + + +// ------------------------------------------------------------------------------------------------ +// Parses the given string for cdata areas and entitys. If a cdata area is +// found, the data in it is skipped over. The entities outside cdata are converted +// into characters they represent (but only if cdata is found). +// Note: This method is intended for processing the CDATA used right after . +// I.e. this method removes the cdata-elements used for wrapping the whole xml data +// and processes the string so that the inner cdatas used in the xml become valid. +// THIS METHOD SHOULD BE CALLED RIGHT IN THE BEGINNING OF PARSING IF -BLOCK +// IS WRAPPED WITHIN CDATA. +// ------------------------------------------------------------------------------------------------ +void CNSmlXmlParser::PreProcessL( HBufC8* aXml ) const + { + // take a modifiable pointer + TPtr8 xml = aXml->Des(); + + // the string used for searching and moving in the string + TPtrC8 searchString(*aXml); + + // the current position in the original string + TInt searchStartPos = 0; + + // find the first cdata start + TInt cdataStartPos = searchString.Find(KCDataStart); + + // If CDATA is not found from beginning then data is not inside CDATA and then + // preprocessing is not needed + if ( cdataStartPos != 0 ) + { + return; + } + + TInt cdataEndPos = KErrNotFound; + + // while cdata is found + while ( cdataStartPos != KErrNotFound ) + { + cdataStartPos += searchStartPos; + + // find an end of cdata before entities are converted + cdataEndPos = searchString.Find(KCDataEnd); + + // convert entities between search start and cdata start + TInt entityChange = EntitiesToCharactersL(aXml, searchStartPos, cdataStartPos); + xml.Set(aXml->Des()); + cdataStartPos += entityChange; + + if ( cdataEndPos != KErrNotFound ) + { + cdataEndPos += entityChange; + cdataEndPos += searchStartPos; + + // if the end is before start -> error + if (cdataEndPos < cdataStartPos) + User::Leave( EInvalidCDataStructure ); + + // remove cdata end + xml.Delete(cdataEndPos, KCDataEnd().Length()); + // remove cdata start + xml.Delete(cdataStartPos, KCDataStart().Length()); + + searchStartPos = cdataEndPos - KCDataStart().Length(); + searchString.Set( xml.Right(xml.Length() - searchStartPos) ); + } + else + { + // the end of cdata was not found or cdata end was before start -> error + User::Leave( EInvalidCDataStructure ); + } + + // find a new cdata start that is after the found end + cdataStartPos = searchString.Find(KCDataStart); + + } + + } + + +// ------------------------------------------------------------------------------------------------ +// Return the TNSmlBoolean-value's string-representation. +// ------------------------------------------------------------------------------------------------ +TPtrC8 CNSmlXmlParser::BooleanToString( const TNSmlBoolean aValue ) const + { + TPtrC8 str; + switch(aValue) + { + case EBooleanTrue: + { + str.Set(KStringTrue); + break; + } + case EBooleanFalse: + { + str.Set(KStringFalse); + break; + } + default: + { + str.Set(KStringEmpty); + } + } + + return str; + } + + +// ------------------------------------------------------------------------------------------------ +// Returns the TTime-value's string-representation. +// ------------------------------------------------------------------------------------------------ +TBuf8 CNSmlXmlParser::DateTimeToStringL( const TTime& aValue ) const + { + TBuf str; + + // aValue is not changed but Z character is added to the end of string. + // Messaging uses UTC times and that is reason why time is not changed. + aValue.FormatL( str, _L("%F%Y%M%DT%H%T%SZ") ); + + // from 16-bit to 8-bit + TBuf8 dt; + dt.Copy(str); + + return dt; + } + + +// ------------------------------------------------------------------------------------------------ +// Returns the integer value's string-representation. +// ------------------------------------------------------------------------------------------------ +TBuf8 CNSmlXmlParser::IntegerToString( const TInt aValue ) const + { + TBuf8 str; + str.AppendNum(aValue); + return str; + } + + +// ------------------------------------------------------------------------------------------------ +// Returns the boolean representation of the string or leaves if error (EInvalidBooleanValue). +// ------------------------------------------------------------------------------------------------ +TNSmlBoolean CNSmlXmlParser::StringToBooleanL( const TPtrC8& aValue ) const + { + if( aValue == KStringTrue ) + return EBooleanTrue; + else if( aValue == KStringFalse || aValue == KStringEmpty ) + return EBooleanFalse; + else + User::Leave(EInvalidBooleanValue); + + return EBooleanMissing; + } + + +// ------------------------------------------------------------------------------------------------ +// Returns the integer representation of the string or leaves if error (EInvalidIntegerValue). +// ------------------------------------------------------------------------------------------------ +TInt CNSmlXmlParser::StringToIntegerL( const TPtrC8& aValue ) const + { + if (aValue.Length() <= 0) + return 0; + + // convert the data to an integer + TLex8 lex(aValue); + TUint uValue = 0; + TBool isNegative = EFalse; + + TChar c = lex.Peek(); + + // check for a minus or plus sign + if ( c == '-' ) + { + isNegative = ETrue; + lex.Inc(); + } + else if ( c == '+' ) + { + lex.Inc(); + } + + TRadix radix = EDecimal; + c = lex.Peek(); + + if (c == '0') // octal or hex + { + lex.Get(); + c = lex.Get(); + if ( c == 'x' || c == 'X' ) + { + radix = EHex; + } + else + { + radix = EOctal; + lex.UnGet(); // back up + } + } + + TInt err = lex.Val(uValue, radix); + if ( err != KErrNone ) + User::Leave(EInvalidIntegerValue); + + TInt value = uValue; + + return isNegative ? value*(-1) : value; + } + + +// ------------------------------------------------------------------------------------------------ +// Returns the TTime representation of the string or leaves if error (EInvalidDatetimeValue). +// UTC times are not supported, i.e. datetimes that have Z-ending are treated as +// local times. +// ------------------------------------------------------------------------------------------------ +TTime CNSmlXmlParser::StringToTTimeL( TPtrC8& aValue ) const + { + // check that there is data + if (aValue.Length() <= 0) + User::Leave( EInvalidDatetimeValue ); + + // format the data into a TTime + + if (aValue[aValue.Length()-1] == 'Z') + { + // The datetime is in UTC, which is not supported + // no correction done, treat as local time + aValue.Set( aValue.Left( aValue.Length()-1 ) ); + } + + TDateTime datetime(0,(TMonth)0,0,0,0,0,0); + + // read datetime and check errors + + TInt error = KErrNone; + + // read year + TPtrC8 str = aValue.Left(4); + TLex8 lex(str); + TInt value; + error = lex.Val(value); + CheckDatetimeErrorL( error ); + error = datetime.SetYear(value); + CheckDatetimeErrorL( error ); + + // read month + str.Set(aValue.Mid(4, 2)); + lex.Assign(str); + error = lex.Val(value); + CheckDatetimeErrorL( error ); + --value; + error = datetime.SetMonth((TMonth)value); + CheckDatetimeErrorL( error ); + + // read day + str.Set(aValue.Mid(6, 2)); + lex.Assign(str); + error = lex.Val(value); + CheckDatetimeErrorL( error ); + --value; + error = datetime.SetDay(value); + CheckDatetimeErrorL( error ); + + // Skip character 'T' and read hour + str.Set(aValue.Mid(9, 2)); + lex.Assign(str); + error = lex.Val(value); + CheckDatetimeErrorL( error ); + error = datetime.SetHour(value); + CheckDatetimeErrorL( error ); + + // minutes + str.Set(aValue.Mid(11, 2)); + lex.Assign(str); + error = lex.Val(value); + CheckDatetimeErrorL( error ); + error = datetime.SetMinute(value); + CheckDatetimeErrorL( error ); + + // seconds + str.Set(aValue.Mid(13, 2)); + lex.Assign(str); + error = lex.Val(value); + CheckDatetimeErrorL( error ); + error = datetime.SetSecond(value); + CheckDatetimeErrorL( error ); + + return TTime(datetime); + } + + +// ------------------------------------------------------------------------------------------------ +// Adds start element, the value and end element to aPtr. +// ------------------------------------------------------------------------------------------------ +void CNSmlXmlParser::AppendElement( TPtr8& aPtr, const TDesC8& aElementName, const TDesC8& aValue ) const + { + // start element + aPtr.Append(KElementStart); + aPtr.Append(aElementName); + aPtr.Append(KElementEnd); + + // value + aPtr.Append(aValue); + + // end element + aPtr.Append(KElementStart); + aPtr.Append(KCharacterSlash); + aPtr.Append(aElementName); + aPtr.Append(KElementEnd); + } + + +// ------------------------------------------------------------------------------------------------ +// Forms an element using the given element name and appends it to the given string. +// ------------------------------------------------------------------------------------------------ +void CNSmlXmlParser::AppendElement( TPtr8& aPtr, const TDesC8& aElementName ) const + { + aPtr.Append(KElementStart); + aPtr.Append(aElementName); + aPtr.Append(KElementEnd); + } + + +// ------------------------------------------------------------------------------------------------ +// Forms an end element using the given element name and appends it to the given string. +// ------------------------------------------------------------------------------------------------ +void CNSmlXmlParser::AppendEndElement( TPtr8& aPtr, const TDesC8& aElementName ) const + { + aPtr.Append(KElementStart); + aPtr.Append(KCharacterSlash); + aPtr.Append(aElementName); + aPtr.Append(KElementEnd); + } + + +// ------------------------------------------------------------------------------------------------ +// Returns the total length of start and end tag. +// ------------------------------------------------------------------------------------------------ +TInt CNSmlXmlParser::SizeOfElements( const TDesC8& aElementName ) const + { + TInt size = 0; + + // start element plus end element ( 1 = length of '/' char ) + size += 2*aElementName.Length() + 1; + size += 2*KElementStartEndWidth; // '<' and '>' + + return size; + } + + +// ------------------------------------------------------------------------------------------------ +// Returns the length of the given boolean element and it's data if it was a string. +// ------------------------------------------------------------------------------------------------ +TInt CNSmlXmlParser::SizeOfBoolean( const TNSmlBoolean aValue, const TDesC8& aElementName ) const + { + TInt size = 0; + size += SizeOfElements( aElementName ); + switch ( aValue ) + { + case EBooleanTrue: + return size+4; // "true" + case EBooleanFalse: + return size+5; // "false" + default: + return 0; + } + } + + +// ------------------------------------------------------------------------------------------------ +// Returns the length of the given datetime element and it's data if it was a string. +// ------------------------------------------------------------------------------------------------ +TInt CNSmlXmlParser::SizeOfDatetime( const TDesC8& aElementName ) const + { + TInt size = SizeOfElements( aElementName ) + KDateTimeLength; + return size; + } + + +// ------------------------------------------------------------------------------------------------ +// Returns the maximum length of the given integer element and it's data if it was a string. +// ------------------------------------------------------------------------------------------------ +TInt CNSmlXmlParser::SizeOfInteger( const TDesC8& aElementName ) const + { + TInt size = SizeOfElements( aElementName ) + KIntegerMaxLength; + return size; + } + + +// ------------------------------------------------------------------------------------------------ +// Returns the length of the given string element and it's data if it was a string. +// ------------------------------------------------------------------------------------------------ +TInt CNSmlXmlParser::SizeOfString( const HBufC8* aValue, const TDesC8& aElementName ) const + { + TInt size = SizeOfElements( aElementName ) + aValue->Length(); + return size; + } + + +// ------------------------------------------------------------------------------------------------ +// Finds entities and replaces them with the characters they represent. Returns +// an integer indicating the size change in aXml. +// ------------------------------------------------------------------------------------------------ +TInt CNSmlXmlParser::EntitiesToCharactersL( HBufC8*& aXml, TInt aStartPos, TInt aEndPos ) const + { + TInt change = 0; + TInt changeSum = 0; + + change = ReplaceL(aXml, KEntityLT, KLessThan, aStartPos, aEndPos); + changeSum += change; + aEndPos += change; + + change = ReplaceL(aXml, KEntityGT, KGreaterThan, aStartPos, aEndPos); + changeSum += change; + aEndPos += change; + + change = ReplaceL(aXml, KEntityAMP, KAmpersand, aStartPos, aEndPos); + changeSum += change; + aEndPos += change; + + change = ReplaceL(aXml, KEntityAPOS, KApostrophe, aStartPos, aEndPos); + changeSum += change; + aEndPos += change; + + change = ReplaceL(aXml, KEntityQUOT, KQuotation, aStartPos, aEndPos); + changeSum += change; + + return changeSum; + } + + +// ------------------------------------------------------------------------------------------------ +// Finds special characters and replaces them with corresponding entities. Returns +// an integer indicating the size change in aXml. +// ------------------------------------------------------------------------------------------------ +TInt CNSmlXmlParser::CharactersToEntitiesL( HBufC8*& aXml, TInt aStartPos, TInt aEndPos ) const + { + TInt change = 0; + TInt changeSum = 0; + + // Note: this replace has to be the first one, since it changes + // &-characters to &s and all the other replaces generate + // &-characters as they are entities. + change = ReplaceL(aXml, KAmpersand, KEntityAMP, aStartPos, aEndPos); + changeSum += change; + aEndPos += change; + + change = ReplaceL(aXml, KLessThan, KEntityLT, aStartPos, aEndPos); + changeSum += change; + aEndPos += change; + + change = ReplaceL(aXml, KGreaterThan, KEntityGT, aStartPos, aEndPos); + changeSum += change; + aEndPos += change; + + change = ReplaceL(aXml, KApostrophe, KEntityAPOS, aStartPos, aEndPos); + changeSum += change; + aEndPos += change; + + change = ReplaceL(aXml, KQuotation, KEntityQUOT, aStartPos, aEndPos); + changeSum += change; + + return changeSum; + } + + +// ------------------------------------------------------------------------------------------------ +// Returns ETrue if all the characters in the given text are whitespace +// characters, else EFalse. +// ------------------------------------------------------------------------------------------------ +TBool CNSmlXmlParser::IsWhitespace( const TDesC8& aText ) const + { + // loop the string character by character + TText c; + for ( TInt i=0; i < aText.Length(); ++i ) + { + c = aText[i]; + switch( c ) + { + case KWhitespaceEmpty: + break; + case KWhitespaceLineFeed: + break; + case KWhitespaceNewLine: + break; + case KWhitespaceTabular: + break; + case KWhitespaceLineFeedNewLine: + break; + default: + return EFalse; + } + } + + return ETrue; + } + + +// ------------------------------------------------------------------------------------------------ +// Leaves with EInvalidXmlError if the given string is not whitespace. +// ------------------------------------------------------------------------------------------------ +void CNSmlXmlParser::LeaveIfNotWhiteSpaceL( const TDesC8& aText ) const + { + if ( !IsWhitespace( aText ) ) + { + User::Leave( EInvalidXmlError ); + } + } + + +// ------------------------------------------------------------------------------------------------ +// Maps the given value to TNSmlParserGeneratorError. +// ------------------------------------------------------------------------------------------------ +TNSmlParserGeneratorError CNSmlXmlParser::CheckError( const TInt error ) const + { + if ( error == KErrNoMemory ) + { + return EOutOfMemory; + } + else if ( error < KErrNone ) + { + // some system wide error, should not occur + return EUnknownError; + } + else if ( error != EErrorNone ) + { + return (TNSmlParserGeneratorError)error; + } + + return EErrorNone; + } + + +// ------------------------------------------------------------------------------------------------ +// Parses the given xml and calls NextDataL and NextElementL methods when finds +// data or element. +// ------------------------------------------------------------------------------------------------ +void CNSmlXmlParser::ParseL( TPtrC8& aXml ) + { + + ResetBufferL(iBuffer); + ResetBufferL(iCompleteBuffer); + + // boolean indicating if reading element name (true) or insides of + // an element (false) + TBool readingElementName = EFalse; + TText c; + + for( TInt i=0; i < aXml.Length(); ++i ) + { + c = aXml[i]; + switch(c) + { + case KElementStart: + // if currently reading element, error + if( readingElementName ) + { + User::Leave(EInvalidXmlError); + } + + if( aXml.Length()-i >= KCDataStart().Length() && + !aXml.Mid(i, KCDataStart().Length()).Compare(KCDataStart()) ) + { // cdata + aXml.Set( aXml.Right( aXml.Length() - i ) ); + TInt endPos = aXml.Find(KCDataEnd); + + TPtrC8 cdata = _L8(""); + if ( endPos == KErrNotFound ) + { + User::Leave(EInvalidCDataStructure); + } + + cdata.Set( aXml.Mid( KCDataStart().Length(), endPos - KCDataStart().Length() ) ); + aXml.Set( aXml.Right( aXml.Length() - endPos - KCDataEnd().Length() ) ); + + i = -1; + + // add current buffer to complete buffer + EntitiesToCharactersL( iBuffer, 0, iBuffer->Length() ); + AddToCompleteL(*iBuffer); + ResetBufferL(iBuffer); + AddToCompleteL(cdata); + } +#ifndef __NO_XML_COMMENTS_ + else if( aXml.Length()-i >= KCommentStart().Length() && + !aXml.Mid(i, KCommentStart().Length()).Compare(KCommentStart()) ) + { // comment + aXml.Set( aXml.Right( aXml.Length() - i ) ); + TInt endPos = aXml.Find(KCommentEnd); + + if ( endPos != KErrNotFound ) + { + aXml.Set( aXml.Right( aXml.Length() - endPos - KCommentEnd().Length() ) ); + } + else + { + User::Leave(EInvalidXmlError); + } + + i = -1; + } +#endif + else + { + // send the buffer + EntitiesToCharactersL( iBuffer, 0, iBuffer->Length() ); + AddToCompleteL(*iBuffer); + NextDataL(*iCompleteBuffer); + ResetBufferL(iBuffer); + ResetBufferL(iCompleteBuffer); + readingElementName = ETrue; + } + + break; + case KElementEnd: + // stop reading element name + if( !readingElementName ) + { + User::Leave(EInvalidXmlError); + } + else + { + NextElementL(*iBuffer); + ResetBufferL(iBuffer); + readingElementName = EFalse; + } + break; + default: + // add char to buffer + AddToBufferL(c, iBuffer); + break; + } + } + + EntitiesToCharactersL( iBuffer, 0, iBuffer->Length() ); + AddToCompleteL(*iBuffer); + NextDataL(*iCompleteBuffer); + } + + +// ------------------------------------------------------------------------------------------------ +// Adds the given string to iCompleteBuffer +// ------------------------------------------------------------------------------------------------ +void CNSmlXmlParser::AddToCompleteL( const TPtrC8 aStr ) + { + if(!iCompleteBuffer) + { + iCompleteBuffer = HBufC8::NewL(aStr.Length()); + } + + TPtr8 ptr = iCompleteBuffer->Des(); + if( ptr.MaxLength() < iCompleteBuffer->Length()+aStr.Length() ) + { + iCompleteBuffer = iCompleteBuffer->ReAllocL(iCompleteBuffer->Length()+aStr.Length()); + ptr.Set(iCompleteBuffer->Des()); + } + + ptr.Append(aStr); + } + + +// ------------------------------------------------------------------------------------------------ +// Deletes the given buffer and initializes it again to length 10 +// ------------------------------------------------------------------------------------------------ +void CNSmlXmlParser::ResetBufferL( HBufC8*& aBuf ) const + { + if( aBuf ) + { + delete aBuf; + aBuf = NULL; + } + + aBuf = HBufC8::NewL(10); + } + + +// ------------------------------------------------------------------------------------------------ +// Adds the given char to given buffer +// ------------------------------------------------------------------------------------------------ +void CNSmlXmlParser::AddToBufferL( const TText c, HBufC8*& aBuf ) const + { + TPtr8 ptr = aBuf->Des(); + if( ptr.MaxLength() == aBuf->Length()+1 ) + { + aBuf = aBuf->ReAllocL(aBuf->Length()+10); + ptr.Set(aBuf->Des()); + } + + TChar str = c; + ptr.Append(str); + } + + + +// ------------------------------------------------------------------------------------------------ +// Replaces all occurances of aTarget in aText with aItem. Returns an integer indicating +// the size change in aText. aStartPos and aEndPos indicate the start and end positions +// of aText to be parsed (if whole string should be parsed, use 0 and aText.Length()). +// ------------------------------------------------------------------------------------------------ +TInt CNSmlXmlParser::ReplaceL( HBufC8*& aText, const TDesC8& aTarget, const TDesC8& aItem, TInt aStartPos, TInt aEndPos ) const + { + TInt change = 0; + TInt searchPos = aStartPos; + TPtrC8 text = aText->Mid(aStartPos, aEndPos-aStartPos); + TInt pos = text.Find(aTarget); + + while ( pos != KErrNotFound ) + { + pos += searchPos; + + TInt currentChange = aItem.Length() - aTarget.Length(); + change += currentChange; + aEndPos += currentChange; + searchPos = pos; + + if ( currentChange > 0 ) + { + searchPos += currentChange; + + // check that aText is large enough + if ( aText->Des().MaxLength() < aText->Length()+currentChange ) + { + aText = aText->ReAllocL(aText->Length()+currentChange); + } + } + + // the actual replace + aText->Des().Replace(pos, aTarget.Length(), aItem); + + text.Set( aText->Mid(searchPos, aEndPos-searchPos) ); + pos = text.Find(aTarget); + } + + return change; + } + + +// ------------------------------------------------------------------------------------------------ +// +// ------------------------------------------------------------------------------------------------ +void CNSmlXmlParser::CheckDatetimeErrorL( const TInt error ) const + { + if ( error != KErrNone ) + { + User::Leave( EInvalidDatetimeValue ); + } + } + +//End of File +