/*
* Copyright (c) 1999-2009 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:
*
*/
#include "TXTSTD.H"
#include "ParseLst.h"
//++ sort out definitive hash includes
CParserList::CParserItem::CParserItem(MParser* aParser, const TUint aTagIndex)
: iParser(aParser),
iTagIndex(aTagIndex)
{
}
// Panic the process with UikParse as the category.
void CParserList::Panic(TParserListPanic aPanic) const
{
_LIT(panicStr, "ParseLst");
User::Panic(panicStr, aPanic);
}
CParserList::CParserList()
: iParserList(4)
{
iHighestIndex = 0;
iNumberInList = 0;
}
CParserList::~CParserList()
{
iParserList.ResetAndDestroy(); // Reset the list and destroy the CParserItems
iParserList.Close(); // but leave the parsers and free the resources
}
// Add parser to appropriate list
void CParserList::ActivateAParserL(MParser* aParser, const TBool aDefaultParser)
{
// Check the index isn't rolling over
// N.B. This is somewhat unlikely. Since index is effectively a 29-bit
// uint (I'm using the top 3 bits as flags) this allows 268435456 parsers
// to be installed. (Assuming we deinstall almost as many, as we went
// along, to avoid OOM.) At 50 per sec that's 20 years continuous action!
__ASSERT_DEBUG((iHighestIndex < EMaxParserIndex), Panic(EParserIndexRollover));
__ASSERT_DEBUG(aParser, Panic(EParserNullPtr));
TUint parserIndex = iHighestIndex + 1;
// Adjust flags to describe parser
if (aParser->ReformatOnRecognise())
parserIndex |= static_cast<TUint>( EReformatOnRecogniseFlag );
if (aParser->ReformatOnRollover())
parserIndex |= EReformatOnRolloverFlag;
if (aDefaultParser)
parserIndex |= EDefaultParserFlag;
// Create a parser item
TInt result;
CParserItem* parserItem = new (ELeave) CParserItem(aParser, parserIndex);
++iHighestIndex;
if (aDefaultParser)
{
CleanupStack::PushL(parserItem);
result = iParserList.Append(parserItem);
if (result) // We couldn't get it in the main list
User::Leave(result);
CleanupStack::Pop();
}
else
{
// Look for the end of the specific parsers and the start of the default set
TInt count;
for (count = 0; count < iNumberInList; count++)
{
if (DefaultParser(iParserList[count]->TagIndex()))
break;
}
CleanupStack::PushL(parserItem);
result = iParserList.Insert(parserItem, count);
if (result) // We couldn't get it in the main list
User::Leave(result);
CleanupStack::Pop();
}
iNumberInList++;
}
// Activate an individual parser
void CParserList::ActivateParserL(MParser* aParser)
{
ActivateAParserL(aParser, EFalse);
}
// N.B. We can't just delete CParserList and zero TLS when we get back
// to a list with no specific or default parsers because there could be
// an EText left with a local ptr to it.
void CParserList::DeactivateParser(MParser* aParser)
{
__ASSERT_DEBUG(iNumberInList, Panic(EParserNoneActive));
__ASSERT_DEBUG(aParser, Panic(EParserNullPtr));
// Run thru list till find entry we need
TInt count;
for (count = 0; count < iNumberInList; count++)
{
if (iParserList[count]->Parser() == aParser)
{
delete iParserList[count];
iParserList.Remove(count);
break;
}
}
__ASSERT_DEBUG((count < iNumberInList), Panic(EParserInstanceNotActive));
iNumberInList--;
}
// Activate a parser as one of the default set
void CParserList::ActivateDefaultParserL(MParser* aParser)
{
ActivateAParserL(aParser, ETrue);
}
//++ Put comment here
void CParserList::DeactivateParserDefaults()
{
if (iNumberInList)
{
// Take them out of the list
while (iNumberInList && DefaultParser(iParserList[iNumberInList - 1]->TagIndex()))
{
// Tell the parser to free itself
iParserList[iNumberInList - 1]->Parser()->Release();
// Delete the item that refers to it
delete iParserList[iNumberInList - 1];
// Remove the entry from the list
iParserList.Remove(iNumberInList - 1);
iNumberInList--;
}
}
}
// Called by EText to scan an area of text
TBool CParserList::ParseThisText(CRichText& aTextObj,TInt aStartScan,TInt aScanLength,TInt& aStartOfTags,TInt& aLength)
{
TBool foundSomething = EFalse;
TCharFormatX format;
TCharFormatXMask varies;
TPtrC ptr;
TInt endRange;
// Scan either side of the range in case part of some tagged text was deleted.
if (aStartScan > 0)
{
aStartScan--;
aScanLength++;
}
if (aStartScan + aScanLength < aTextObj.DocumentLength())
aScanLength++;
if (iNumberInList && aScanLength)
{
aStartOfTags = aStartScan + aScanLength;
aLength = 0;
for (TInt count = 0; count < iNumberInList; count++)
{
// For each parser in the list
TUint tagIndex = iParserList[count]->TagIndex();
TInt pos;
MParser* parser = iParserList[count]->Parser();
TInt localStartScan = aStartScan;
TInt localScanLength = aScanLength;
// Start by removing existing tags for this parser. This ensures
// that old tags that have been invalidated by subsequent editing
// are removed. Any that are still valid will be replaced.
aTextObj.GetExtendedCharFormat(format, varies, localStartScan, aScanLength);
if (format.iParserTag || varies.AttribIsSet(EAttParserTag))
{
// At least some of the object contains a non-zero tag - go through it
// Are we starting part way through a tag?
aTextObj.GetExtendedCharFormat(format, varies, localStartScan, 1);
if (format.iParserTag == tagIndex)
{
// The first char of this range has the current parsers tag
// so we need to check backwards for the start of that tag
for (pos = localStartScan; pos > 0; pos--)
{
aTextObj.GetExtendedCharFormat(format, varies, pos - 1, 1);
if (format.iParserTag != tagIndex)
break;
}
// Since we are going to remove a tag starting from here
// we need to allow this area to be rescanned
localScanLength += localStartScan - pos;
localStartScan = pos;
}
// What about off the end?
aTextObj.GetExtendedCharFormat(format, varies, localStartScan + localScanLength - 1, 1);
if (format.iParserTag == tagIndex)
{
// The last char of this range has the current parsers tag
// so we need to check forwards for the end of that tag
pos = localStartScan + localScanLength;
TInt end = aTextObj.DocumentLength();
while (pos < end)
{
aTextObj.GetTextAndExtendedFormat(ptr, format, pos);
if (format.iParserTag != tagIndex)
break;
pos += ptr.Length();
}
// Adjust scan length
localScanLength = pos - localStartScan;
}
pos = localStartScan;
while (pos < localStartScan + localScanLength)
{
// Run along the scan range
aTextObj.GetTextAndExtendedFormat(ptr, format, pos);
if (format.iParserTag == tagIndex)
{
// Remove this tag
format.iParserTag = 0;
varies.ClearAll();
varies.SetAttrib(EAttParserTag);
TRAPD(leaveCode, aTextObj.ApplyExtendedCharFormatL(format, varies, pos, ptr.Length()));
if (leaveCode==KErrNone)
foundSomething = ETrue; // We are removing a tag
if (aLength)
{
if (pos < aStartOfTags)
{
aLength += aStartOfTags - pos;
aStartOfTags = pos;
}
if (pos + ptr.Length() > aStartOfTags + aLength)
aLength = pos + ptr.Length() - aStartOfTags;
}
else
{
aStartOfTags = pos;
aLength = ptr.Length();
}
}
pos += ptr.Length();
}
}
endRange = localStartScan + localScanLength;
// For this parser, run through text looking for changes
TBool allowBack = ETrue;
for (;;) // Run forever
{
TInt localStartTag = aTextObj.DocumentLength();
TInt localTagLength = 0;
TInt result = parser->ParseThisText(aTextObj, allowBack, localStartScan, localScanLength, localStartTag, localTagLength);
if (!result)
break;
__ASSERT_DEBUG(allowBack || (localStartTag >= localStartScan), Panic(EParserIgnoringAllowFlag));
TInt startNewTag = localStartTag;
TInt lengthNewTag = localTagLength;
aTextObj.GetExtendedCharFormat(format, varies, localStartTag, localTagLength);
if (format.iParserTag || varies.AttribIsSet(EAttParserTag))
{
// At least some of this area contains a non-zero tag - go through it
pos = localStartTag;
TBool higher = EFalse;
while (pos < localStartTag + localTagLength)
{
aTextObj.GetTextAndExtendedFormat(ptr, format, pos);
if (format.iParserTag && (MaskedTag(format.iParserTag) < MaskedTag(tagIndex)))
{
// A higher precedence tag is already here so we can't
// insert our tag - let's see how far it goes
TUint tag = format.iParserTag; // Stash tag before overwriting it
TInt len = aTextObj.DocumentLength();
while (pos < len)
{
aTextObj.GetTextAndExtendedFormat(ptr, format, pos);
if (format.iParserTag != tag)
break;
pos += ptr.Length();
}
result = EFalse;
startNewTag = pos;
lengthNewTag = 0;
break;
}
// If there aren't any higher precedence tags in here then this
// will save us having to go through again if there aren't any
// lower precedence tags either
if (MaskedTag(format.iParserTag) >= MaskedTag(tagIndex))
higher = ETrue;
pos += ptr.Length();
}
if (higher)
{
// There are lower or equal precedence tags in this range
// Do they extend back off the start?
aTextObj.GetExtendedCharFormat(format, varies, localStartTag, 1);
if (format.iParserTag)
{
// need to check backwards
TUint tag = format.iParserTag; // Stash tag before overwriting it
for (pos = localStartTag; pos > 0; pos--)
{
aTextObj.GetExtendedCharFormat(format, varies, pos - 1, 1);
if (format.iParserTag != tag)
break;
}
localTagLength += localStartTag - pos;
localStartTag = pos;
}
// What about off the end?
pos = localStartTag + localTagLength;
aTextObj.GetExtendedCharFormat(format, varies, pos - 1, 1);
if (format.iParserTag)
{
// need to check forwards
TUint tag = format.iParserTag; // Stash tag before overwriting it
TInt len = aTextObj.DocumentLength();
while (pos < len)
{
aTextObj.GetTextAndExtendedFormat(ptr, format, pos);
if (format.iParserTag != tag)
break;
pos += ptr.Length();
}
localTagLength = pos - localStartTag;
}
// Remove all tags in this area - they all have lower precedence
format.iParserTag = 0;
varies.ClearAll();
varies.SetAttrib(EAttCharLanguage);
TRAPD(leaveCode, aTextObj.ApplyExtendedCharFormatL(format, varies, localStartTag, localTagLength));
if (leaveCode==KErrNone)
foundSomething = ETrue; // We are removing a tag
}
}
if (result)
{
// Format tag this area with tagIndex
format.iParserTag = tagIndex;
varies.ClearAll();
varies.SetAttrib(EAttParserTag);
// Use the original length, not the possibly expanded version
TRAPD(leaveCode, aTextObj.ApplyExtendedCharFormatL(format, varies, startNewTag, lengthNewTag));
if (leaveCode==KErrNone)
foundSomething = ETrue; // We are applying a tag
if (aLength)
{
if (localStartTag < aStartOfTags)
{
aLength += aStartOfTags - localStartTag;
aStartOfTags = localStartTag;
}
if (localStartTag + localTagLength > aStartOfTags + aLength)
aLength = localStartTag + localTagLength - aStartOfTags;
}
else
{
aStartOfTags = localStartTag;
aLength = localTagLength;
}
}
// Jump over
localScanLength -= startNewTag + lengthNewTag - localStartScan;
localStartScan = startNewTag + lengthNewTag; // Adjust start of next scan run
if (localStartScan >= endRange) // Have we reached the end of the range yet?
break;
allowBack = EFalse;
}
}
}
return foundSomething;
}
// given a tag, fetch a ptr to the parser - or null
MParser* CParserList::ParserWithThisTag(const TUint aTagIndex) const
{
MParser* parser = NULL;
for (TInt count = 0; count < iNumberInList; count++)
{
if (aTagIndex == iParserList[count]->TagIndex())
{
parser = iParserList[count]->Parser();
break;
}
}
return parser;
}
// given a ptr to a parser, fetch a tag - or zero
TUint CParserList::TagForThisParser(const MParser *const aParser) const
{
TUint tagIndex = 0;
for (TInt count = 0; count < iNumberInList; count++)
{
if (aParser == iParserList[count]->Parser())
{
tagIndex = iParserList[count]->TagIndex();
break;
}
}
__ASSERT_DEBUG(tagIndex, Panic(EParserNoSuchTag));
return tagIndex;
}
CParserData::CParserData(TInt aEndParse):
iStartParse(0),
iEndParse(aEndParse)
{
// Get parser data
iActiveParserList = (CParserList*)Dll::Tls();
iLastKnownCursor = -1;
if (iActiveParserList)
iActiveParserList->iRefCount++;
}
CParserData::~CParserData()
{
if (iActiveParserList)
{
iActiveParserList->iRefCount--;
if ((iActiveParserList->iRefCount == 0) && (iActiveParserList->iNumberInList == 0))
{
Dll::FreeTls();
delete iActiveParserList;
}
}
}
// Merge the specified range, which may have changed length, into the current range.
// aOldLength is the # of chars deleted and to be removed from the range
// aNewLength is the # of chars inserted and to be added to the range
void CParserData::MergeRange(TInt aStart,TInt aOldLength,TInt aNewLength)
{
if (iStartParse == -1) // no current range
{
iStartParse = aStart;
iEndParse = aStart + aNewLength;
}
else
{
if (aStart < iStartParse)
iStartParse = aStart;
if (aStart + aOldLength > iEndParse)
iEndParse = aStart + aOldLength;
iEndParse += aNewLength - aOldLength;
}
}