/*
* Copyright (c) 1997-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 <txtetext.h>
#include <txtglobl.h>
#include <txtfmlyr.h>
#include <s32mem.h>
#include <s32file.h>
#include <e32test.h>
#include <fldbase.h>
#include <fldbltin.h>
#include <flddef.h>

const TInt KTestCleanupStack=0x20;
const TInt KTestExpandSize=0x20;

LOCAL_D CTrapCleanup* TheTrapCleanup;
LOCAL_D RTest test(_L("T_CONVS - EditableText Persistence"));
LOCAL_D	TPtrC bigBuf(_L("This is a very big buffer indeed, containing text and special characters,\
 big enough to fill a segment of an editable text component that employs segmented storage"));

////////////////////////////////////////////////////////////////////////////////////////////
class TTestFieldFactory : public MTextFieldFactory
	{
public:
	// from MTextFieldFactory
	virtual CTextField* NewFieldL(TUid aFieldType); 
	// Creates a field of the type specified
	// Returns NULL if it does not recognise/support the field type
	};

CTextField* TTestFieldFactory::NewFieldL(TUid aFieldType)
// Creates a field (in aHeader) of the type specified in aHeader
// 
	{
	CTextField* field=NULL;
	if (aFieldType==KDateTimeFieldUid)
		field = (CTextField*)new(ELeave) CDateTimeField();
	return field;
	}
/////////////////////////////////////////////////////////////////////////////////////////////

template <class T>
void testCopy(T &aCopy,const T &anOriginal)
//
// Copy anOriginal to aCopy using memory-based streams.
//
	{
	CBufSeg *buf=CBufSeg::NewL(KTestExpandSize);
	if (buf==NULL)
		test.Panic(_L("Allocating buffer"));
	
//	Write anOriginal out to the buffer.
	RBufWriteStream out(*buf);
	TRAPD(r,out<<anOriginal);
	test(r==KErrNone);
	TRAP(r,out.CommitL());
	if (r!=KErrNone)
			test.Panic(_L("Committing write stream"));

//	Read anOriginal in from the buffer.
	RBufReadStream in(*buf);
	TRAP(r,in>>aCopy);
	test(r==KErrNone);

//	See if it's consumed the lot.
	TRAP(r,in.ReadUint8L());
	test(r==KErrEof);
//
	delete buf;
	}

_LIT(KOutputFile, "c:\\etext\\t_convs.tst");
template <class T>
void testStoreRestoreL(T& aCopy,const T& aOriginal)
// Test document persistance.
//
    {
	// set up the store
	RFs	theFs;
	theFs.Connect();
	//
	theFs.Delete(KOutputFile);
	theFs.MkDirAll(KOutputFile);
	CFileStore* theStore=CDirectFileStore::CreateL(theFs,KOutputFile,EFileRead|EFileWrite);
	CleanupStack::PushL(theStore);
	theStore->SetTypeL(KDirectFileStoreLayoutUid);
	//
	// store the original
	TStreamId id(0);
	TRAPD(ret,id=aOriginal.StoreL(*theStore));
		test(ret==KErrNone);
	//
	// restore into the copy
	TRAP(ret,aCopy.RestoreL(*theStore,id));
		test(ret==KErrNone);
	//
	// tidy up
	CleanupStack::PopAndDestroy();  // theStore
	theFs.Close();
    }


template <class T>
void testCopyChain(T &aCopy,const T &anOriginal,TInt aExcludeCount,const CFormatLayer* aBase)
//
// Copy anOriginal to aCopy using memory-based streams.
//
	{
	CBufSeg *buf=CBufSeg::NewL(KTestExpandSize);
	if (buf==NULL)
		test.Panic(_L("Allocating buffer"));
	
//	Write anOriginal out to the buffer.
	RBufWriteStream out(*buf);
	TRAPD(r,anOriginal.ExternalizeChainL(out,aExcludeCount));
	test(r==KErrNone);
	TRAP(r,out.CommitL());
	if (r!=KErrNone)
			test.Panic(_L("Committing write stream"));

//	Read anOriginal in from the buffer.
	RBufReadStream in(*buf);
	TRAP(r,aCopy.InternalizeChainL(in,aBase));
	test(r==KErrNone);

//	See if it's consumed the lot.
	TRAP(r,in.ReadUint8L());
	test(r!=KErrNone);
//
	delete buf;
	}


LOCAL_C TInt IsEqual(const CPlainText* aCopy,const CPlainText* aOriginal)
//
// Returns true if aCopy contents matches aOriginal contents.
// Takes account of multiple segments of a segmented text component.
//
	{
	TInt lengthOfOriginal=aOriginal->DocumentLength();
	TInt lengthOfCopy=aCopy->DocumentLength();
	test(lengthOfOriginal==lengthOfCopy);
//
	TPtrC copy,orig;
//
	TInt lengthRead=0;
	while(lengthRead<=lengthOfOriginal)
		{
		copy.Set((aCopy->Read(lengthRead)));
		orig.Set((aOriginal->Read(lengthRead)));
		for (TInt offset=0; offset<orig.Length(); offset++)
			test(copy[offset]==orig[offset]);
		lengthRead+=orig.Length();
		}
	test(lengthRead==lengthOfOriginal+1);
	test(aCopy->FieldCount()==aOriginal->FieldCount());
	return 1;
	}


void testPlainTextL(CEditableText::TDocumentStorage aStorage)
//
// Test streaming CPlainText.
//
	{// Create the plain text components.
	test.Start(_L("Streaming CPlainText"));
	CPlainText* copy=CPlainText::NewL(aStorage);
	CPlainText* testDoc=CPlainText::NewL(aStorage);
	//
	// Set the original - empty
	test.Start(_L("empty."));
	testStoreRestoreL(*copy,*testDoc);
	test(IsEqual(copy,testDoc));
	//	
	test.Next(_L("paragraph delimiter"));
	TRAPD(r,testDoc->InsertL(0,CEditableText::EParagraphDelimiter));
	test(r==KErrNone);
	testStoreRestoreL(*copy,*testDoc);	
	test(IsEqual(copy,testDoc));
	//
	// Next test with tons of text guaranteed to force segment break when using segmented storage.	
	test.Next(_L("big text component"));
	testDoc->InsertL(0,bigBuf);
	testStoreRestoreL(*copy,*testDoc);
	test(IsEqual(copy,testDoc));
	//
	// Now test with field components.
	test.Next(_L("big text doc with field components."));
	TTestFieldFactory factory;
	testDoc->SetFieldFactory(&factory);
	copy->SetFieldFactory(&factory);
	CTextField* field=NULL;
	TRAPD(ret,
	field=factory.NewFieldL(KDateTimeFieldUid));
	test(ret==KErrNone);
	TRAP(ret,
	testDoc->InsertFieldL(0,field,KDateTimeFieldUid));
	test(ret==KErrNone);
	testStoreRestoreL(*copy,*testDoc);
	test(IsEqual(copy,testDoc));
	//
	//
	test.End();
	delete copy;
	delete testDoc;
	}


void testGlobalTextL(CEditableText::TDocumentStorage aStorage)
//
// Test streaming CGlobalText.
//
	{// Create the plain text components.
	test.Next(_L("Streaming CGlobalText"));
	CParaFormatLayer* paraLayer=CParaFormatLayer::NewL();
	CCharFormatLayer* charLayer=CCharFormatLayer::NewL();
	// Set something interesting in the layers:
	CParaFormat* paraFormat1=CParaFormat::NewL(); TParaFormatMask paraMask1;
	TCharFormat charFormat1; TCharFormatMask charMask1;
	paraFormat1->iHorizontalAlignment=CParaFormat::ECenterAlign; paraMask1.SetAttrib(EAttAlignment);
	paraFormat1->iLeftMarginInTwips=4000; paraMask1.SetAttrib(EAttLeftMargin);
	paraLayer->SetL(paraFormat1,paraMask1);
	charFormat1.iFontSpec.iFontStyle.SetPosture(EPostureItalic); charMask1.SetAttrib(EAttFontPosture);
	charFormat1.iFontSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold); charMask1.SetAttrib(EAttFontStrokeWeight);
	charLayer->SetL(charFormat1,charMask1);
	//	
	CGlobalText* copy=CGlobalText::NewL(paraLayer,charLayer,aStorage);
	CGlobalText* testDoc=CGlobalText::NewL(paraLayer,charLayer,aStorage);

//	Set the original - empty
	test.Start(_L("empty."));
	testStoreRestoreL(*copy,*testDoc);
	test(IsEqual(copy,testDoc));
//	
	test.Next(_L("paragraph delimiter"));
	TRAPD(r,testDoc->InsertL(0,CEditableText::EParagraphDelimiter));
	test(r==KErrNone);
	testStoreRestoreL(*copy,*testDoc);
	test(IsEqual(copy,testDoc));

//	Next test with tons of text guaranteed to force segment break when using segmented storage.	
	test.Next(_L("big text component"));
	testDoc->InsertL(0,bigBuf);
	testStoreRestoreL(*copy,*testDoc);
	test(IsEqual(copy,testDoc));
		
	test.End();
	test.End();
	delete copy;
	delete testDoc;
	delete paraLayer;
	delete charLayer;
	delete paraFormat1;
	}


LOCAL_C TInt LayerIsEqual(const CParaFormatLayer* aRestored,const CParaFormatLayer* aOriginal)
//
// Returns true if aRestored contents matches aOriginal contents.
//
	{
	CParaFormat* restored=NULL;  TParaFormatMask rm;
	CParaFormat* original=NULL;  TParaFormatMask om;
	TRAPD(r,restored=CParaFormat::NewL());     test(r==KErrNone);
	TRAP(r,original=CParaFormat::NewL());     test(r==KErrNone);

	aOriginal->SenseL(original,om);
	aRestored->SenseL(restored,rm);

	test(original->IsEqual(*restored));

	delete restored;
	delete original;
	return 1;
	}


LOCAL_C TInt LayerIsEqual(const CCharFormatLayer* aRestored,const CCharFormatLayer* aOriginal)
//
// Returns true if aRestored contents matches aOriginal contents.
//
	{
	TCharFormat restored;  TCharFormatMask rm;
	TCharFormat original;  TCharFormatMask om;

	aOriginal->Sense(original,om);
	aRestored->Sense(restored,rm);

	test(original.IsEqual(restored));
	
	return 1;
	}


void testFmtLayerStoreL()
//
// Test the format layer StoreL().
//
	{
	test.Start(_L("CParaFormatLayer"));
//	Create test layers.
	CParaFormatLayer* pfl1=NULL;
	CParaFormatLayer* restored=NULL;
	CParaFormat* pf1=NULL;
	TRAPD(r,restored=CParaFormatLayer::NewL());     test(r==KErrNone);
	// Force *restored* to allocate storage for iteself by storing a null layer.
	TParaFormatMask rm;     rm.ClearAll(); CParaFormat* rpf=NULL;
	restored->SetL(rpf,rm);
	TRAP(r,pfl1=CParaFormatLayer::NewL());     test(r==KErrNone);
	TRAP(r,pf1=CParaFormat::NewL());     test(r==KErrNone);
	TParaFormatMask pm1;
	pm1.SetAll();  // Sets all but the compound attributes.
//	TEST ONE DEFAULT CASES
	test.Start(_L("Default paragraph format values."));
	TRAP(r,pfl1->SetL(pf1,pm1));     test(r==KErrNone);
	testCopy(*restored,*pfl1);
	test(LayerIsEqual(restored,pfl1));
	test(restored->SenseBase()==pfl1->SenseBase());  // Both should default to based on NULL
//	TEST TWO 
	test.Next(_L("Setting all attributes"));
	pf1->iLeftMarginInTwips=5000; pm1.ClearAll(); pm1.SetAttrib(EAttLeftMargin);
	pf1->iRightMarginInTwips=5001; pm1.SetAttrib(EAttRightMargin);
	pf1->iIndentInTwips=5002;pm1.SetAttrib(EAttIndent);
	pf1->iHorizontalAlignment=CParaFormat::ERightAlign; pm1.SetAttrib(EAttAlignment);
	pf1->iVerticalAlignment=CParaFormat::ECenterAlign; pm1.SetAttrib(EAttVerticalAlignment);
	pf1->iLineSpacingInTwips=5003; pm1.SetAttrib(EAttLineSpacing);
	pf1->iLineSpacingControl=CParaFormat::ELineSpacingAtLeastInTwips; pm1.SetAttrib(EAttLineSpacingControl);
	pf1->iSpaceBeforeInTwips=5004; pm1.SetAttrib(EAttSpaceBefore);
	pf1->iSpaceAfterInTwips=5005; pm1.SetAttrib(EAttSpaceAfter);
	pf1->iKeepTogether=ETrue; pm1.SetAttrib(EAttKeepTogether);
	pf1->iKeepWithNext=ETrue; pm1.SetAttrib(EAttKeepWithNext);
	pf1->iStartNewPage=ETrue; pm1.SetAttrib(EAttStartNewPage);
	pf1->iWidowOrphan=ETrue; pm1.SetAttrib(EAttWidowOrphan);
	pf1->iWrap=EFalse; pm1.SetAttrib(EAttWrap);
	pf1->iBorderMarginInTwips=5006; pm1.SetAttrib(EAttBorderMargin);
	pf1->iDefaultTabWidthInTwips=5007; pm1.SetAttrib(EAttDefaultTabWidth);
	// TopBorder
	TParaBorder top;
	top.iLineStyle=TParaBorder::ESolid;
	top.iThickness=4;
	top.iAutoColor=ETrue;
	pf1->SetParaBorderL(CParaFormat::EParaBorderTop,top);
	pm1.SetAttrib(EAttTopBorder);
	// BottomBorder
	TParaBorder bottom;	
	bottom.iLineStyle=TParaBorder::ESolid;
	bottom.iThickness=4;
	bottom.iAutoColor=ETrue;
	pf1->SetParaBorderL(CParaFormat::EParaBorderBottom,bottom);
	pm1.SetAttrib(EAttBottomBorder);
	// LeftBorder
	TParaBorder left;
	left.iLineStyle=TParaBorder::ESolid;
	left.iThickness=4;
	left.iAutoColor=ETrue;
	pf1->SetParaBorderL(CParaFormat::EParaBorderLeft,left);
	pm1.SetAttrib(EAttLeftBorder);
	// RightBorder
	TParaBorder right;
	right.iLineStyle=TParaBorder::ESolid;
	right.iThickness=4;
	top.iAutoColor=ETrue;
	pf1->SetParaBorderL(CParaFormat::EParaBorderRight,right);
	pm1.SetAttrib(EAttRightBorder);
	// Bullet
	pf1->iBullet=new(ELeave)TBullet;
	TUint charCode=5008;
	pf1->iBullet->iCharacterCode=(TUint8)charCode;
	pf1->iBullet->iHeightInTwips=5009;
	pf1->iBullet->iTypeface.iName=_L("Duncan");
	pf1->iBullet->iTypeface.SetIsProportional(EFalse);
	pf1->iBullet->iTypeface.SetIsSerif(EFalse);
	pm1.SetAttrib(EAttBullet);
	// 2 Tab Stops.
	TTabStop tab1,tab2;
	tab1.iTwipsPosition=5010; tab1.iType=TTabStop::ERightTab;
	tab2.iTwipsPosition=5011; tab2.iType=TTabStop::ECenteredTab;
	pf1->StoreTabL(tab1);
	pf1->StoreTabL(tab2);
	pm1.SetAttrib(EAttTabStop);
//
	TRAP(r,pfl1->SetL(pf1,pm1));
	testCopy(*restored,*pfl1);
	test(LayerIsEqual(restored,pfl1));
	test(restored->SenseBase()==pfl1->SenseBase());  // Both should default to based on NULL
	test.End();
//
	delete pf1;
	delete pfl1;
	delete restored;
//
//	Now the CCharFormatLayer Store/Restore
//
	test.Next(_L("CCharFormatLayer"));
// 	
	test.Start(_L("Setting all attributes"));
//	Create test layers.
	CCharFormatLayer* cfl1=NULL;
	CCharFormatLayer* cRestored=NULL;
	TCharFormat cf1; TCharFormatMask cm1;
//
	TRAP(r,cRestored=CCharFormatLayer::NewL());     test(r==KErrNone);
	// Force *restored* to allocate storage for iteself by storing a null layer.
	TCharFormatMask rcm;     rcm.ClearAll(); TCharFormat rcf;
	cRestored->SetL(rcf,rcm);
//
	TRAP(r,cfl1=CCharFormatLayer::NewL());     test(r==KErrNone);
//	
	TRgb color(20,20,20);
	cf1.iFontPresentation.iTextColor=color;	cm1.SetAttrib(EAttColor);
	cf1.iFontSpec.iTypeface.iName=_L("DUNCANXZE");
	cf1.iFontSpec.iTypeface.SetIsProportional(ETrue);	cm1.SetAttrib(EAttFontTypeface);
	cf1.iFontSpec.iTypeface.SetIsSerif(EFalse);
    cf1.iFontSpec.iFontStyle.SetBitmapType(EMonochromeGlyphBitmap);

	cf1.iFontSpec.iHeight=6000;	cm1.SetAttrib(EAttFontHeight);
	cf1.iFontSpec.iFontStyle.SetPosture(EPostureItalic);	cm1.SetAttrib(EAttFontPosture);
	cf1.iFontSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold);	cm1.SetAttrib(EAttFontStrokeWeight);
	cf1.iFontSpec.iFontStyle.SetPrintPosition(EPrintPosSuperscript);	cm1.SetAttrib(EAttFontPrintPos);
	cf1.iFontPresentation.iUnderline=EUnderlineOn;	cm1.SetAttrib(EAttFontUnderline);
	cf1.iFontPresentation.iStrikethrough=EStrikethroughOn;	cm1.SetAttrib(EAttFontStrikethrough);
	cf1.iFontPresentation.iHighlightColor=color;  cm1.SetAttrib(EAttFontHighlightColor);
	cf1.iFontPresentation.iHighlightStyle=TFontPresentation::EFontHighlightNormal;  cm1.SetAttrib(EAttFontHighlightStyle);
//
	TRAP(r,cfl1->SetL(cf1,cm1));
	test(r==KErrNone);
	testCopy(*cRestored,*cfl1);
	test(LayerIsEqual(cRestored,cfl1));

	TCharFormat rfmt;  
    TCharFormatMask rmask;
	cRestored->Sense(rfmt, rmask);
    test(rfmt.iFontSpec.iFontStyle.BitmapType() == EMonochromeGlyphBitmap);

//
	delete cfl1;
	delete cRestored;
	test.End();
	}


LOCAL_C TInt ChainIsEqual(const CParaFormatLayer* aCopy,const CParaFormatLayer* aOriginal)
//
// Tests that the restored chain is identical to the original chain.
//
	{
	TInt origChainCount=aOriginal->ChainCount();
	/*TInt copyChainCount=*/aCopy->ChainCount();
//	Check the chain heads are equal.
	test(LayerIsEqual(aCopy,aOriginal));
	TInt descendantCount=origChainCount-1;
	
	const CFormatLayer* nextCopyLayer=aCopy->SenseBase();
	const CFormatLayer* nextOrigLayer=aOriginal->SenseBase();
	for (TInt loop=0;loop<descendantCount;loop++)
		{
		test(LayerIsEqual((CParaFormatLayer*)nextCopyLayer,(CParaFormatLayer*)nextOrigLayer));

		nextCopyLayer=nextCopyLayer->SenseBase();
		nextOrigLayer=nextOrigLayer->SenseBase();
		}
	return 1;
	}


LOCAL_C TInt ChainIsEqual(const CCharFormatLayer* aCopy,const CCharFormatLayer* aOriginal)
//
// Tests that the restored chain is identical to the original chain.
//
	{
	TInt origChainCount=aOriginal->ChainCount();
	/*TInt copyChainCount=*/aCopy->ChainCount();
//	Check the chain heads are equal.
	test(LayerIsEqual(aCopy,aOriginal));
	TInt descendantCount=origChainCount-1;
	
	const CFormatLayer* nextCopyLayer=aCopy->SenseBase();
	const CFormatLayer* nextOrigLayer=aOriginal->SenseBase();
	for (TInt loop=0;loop<descendantCount;loop++)
		{
		test(LayerIsEqual((CCharFormatLayer*)nextCopyLayer,(CCharFormatLayer*)nextOrigLayer));

		nextCopyLayer=nextCopyLayer->SenseBase();
		nextOrigLayer=nextOrigLayer->SenseBase();
		}
	return 1;
	}


void DoParaChainL()
//
// Tests the streaming of a chain of format layers
//
	{
	test.Next(_L("Re/StoreChainL()"));
	test.Start(_L("CParaFormatLayer"));
//	Create the chain of para format layers.
	CParaFormatLayer* l1=CParaFormatLayer::NewL();
	CParaFormatLayer* l2=CParaFormatLayer::NewL();
	CParaFormatLayer* l3=CParaFormatLayer::NewL();
	CParaFormatLayer* l4=CParaFormatLayer::NewL();
//	Chain together.
	l1->SetBase(l2);
	l2->SetBase(l3);
	l3->SetBase(l4);
//	Create head of restored format stream, and force it to get storage.	
	CParaFormatLayer* restoredChainHead=CParaFormatLayer::NewL();
	CParaFormat* restoredParaFormat=CParaFormat::NewL();
	TParaFormatMask restoredParaMask;
	restoredParaMask.ClearAll();
	restoredChainHead->SetL(restoredParaFormat,restoredParaMask);
//	General paraformat and its mask.
	CParaFormat* paraFormat=CParaFormat::NewL();
	TParaFormatMask paraMask;
	paraMask.ClearAll();
//	Set layer one stuff
	TTabStop tab1,tab2;
	tab1.iTwipsPosition=5000;	tab2.iTwipsPosition=5001;
	tab1.iType=TTabStop::ERightTab;	tab2.iType=TTabStop::ECenteredTab;
	paraFormat->StoreTabL(tab1);
	paraFormat->StoreTabL(tab2);
	paraMask.SetAttrib(EAttTabStop);
	l1->SetL(paraFormat,paraMask);
	paraMask.ClearAll();
//	Set layer two stuff
	TParaBorder top1;
	top1.iLineStyle=TParaBorder::ESolid;
	top1.iThickness=3;
	top1.iAutoColor=ETrue;
	paraFormat->SetParaBorderL(CParaFormat::EParaBorderTop,top1);
	paraMask.SetAttrib(EAttTopBorder);
	l2->SetL(paraFormat,paraMask);
	paraMask.ClearAll();
//	Set the layer 3 stuff.
	paraFormat->iBullet=new(ELeave)TBullet;
	paraFormat->iBullet->iTypeface.iName=_L("SKELTON");
	paraFormat->iBullet->iTypeface.SetIsProportional(EFalse);
	paraFormat->iBullet->iTypeface.SetIsSerif(EFalse);
	paraFormat->iBullet->iHeightInTwips=3003;
	paraFormat->iBullet->iCharacterCode=32;
	paraMask.SetAttrib(EAttBullet);
	l3->SetL(paraFormat,paraMask);
	paraMask.ClearAll();
// Set the layer 4 stuff.
	paraFormat->iHorizontalAlignment=CParaFormat::EJustifiedAlign;	paraMask.SetAttrib(EAttAlignment);
	paraFormat->iSpaceAfterInTwips=6000;			paraMask.SetAttrib(EAttSpaceAfter);
	paraFormat->iKeepTogether=ETrue;			paraMask.SetAttrib(EAttKeepTogether);
	l4->SetL(paraFormat,paraMask);
// NOW DO IT
	testCopyChain(*restoredChainHead,*l1,0,(const CFormatLayer*)NULL);
	TInt restoredChainCount=restoredChainHead->ChainCount();
	test(ChainIsEqual(restoredChainHead,l1));
//	DESTROY STUFF
	CParaFormatLayer* current=restoredChainHead;
	CParaFormatLayer* next=(CParaFormatLayer*)restoredChainHead->SenseBase();
	delete current;
	for (TInt loop=0;loop<restoredChainCount-1;loop++)	
		{
		current=next;
		next=(CParaFormatLayer*)current->SenseBase();
		delete current;
		}
	delete l1;
	delete l2;
	delete l3;
	delete l4;
	delete paraFormat;
	delete restoredParaFormat;
	}


void DoCharChainL()
//
//
//
	{
	test.Next(_L("CCharFormatLayer"));
//	Create the chain of character format layers.
	CCharFormatLayer* cl1=CCharFormatLayer::NewL();
	CCharFormatLayer* cl2=CCharFormatLayer::NewL();
	CCharFormatLayer* cl3=CCharFormatLayer::NewL();
	CCharFormatLayer* cl4=CCharFormatLayer::NewL();
//	Chain together.
	cl1->SetBase(cl2);
	cl2->SetBase(cl3);
	cl3->SetBase(cl4);
//	Create head of restored format stream, and force it to get storage.	
	CCharFormatLayer* rChar=CCharFormatLayer::NewL();
	TCharFormat restoredCharFormat;
	TCharFormatMask restoredCharMask;
	restoredCharMask.ClearAll();
	rChar->SetL(restoredCharFormat,restoredCharMask);
//	General charformat and its mask.
	TCharFormat charFormat;	TCharFormatMask charMask;
	charMask.ClearAll();
//	Set layer one stuff
	charFormat.iFontSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold);	charMask.SetAttrib(EAttFontStrokeWeight);
	charFormat.iFontSpec.iFontStyle.SetPosture(EPostureItalic);	charMask.SetAttrib(EAttFontPosture);
	charFormat.iFontPresentation.iUnderline=EUnderlineOn;	charMask.SetAttrib(EAttFontUnderline);
	cl1->SetL(charFormat,charMask);
	charMask.ClearAll();
//	Set layer two stuff
	charFormat.iFontSpec.iFontStyle.SetPrintPosition(EPrintPosSubscript);	charMask.SetAttrib(EAttFontPrintPos);
	cl2->SetL(charFormat,charMask);
	charMask.ClearAll();
//	Set the layer 3 stuff.
	charFormat.iFontPresentation.iStrikethrough=EStrikethroughOn;	charMask.SetAttrib(EAttFontStrikethrough);
	cl3->SetL(charFormat,charMask);
	charMask.ClearAll();
//	Set the layer 4 stuff.
	charFormat.iFontSpec.iTypeface.iName=_L("Arial");
	charFormat.iFontSpec.iHeight=200;
	charMask.SetAttrib(EAttFontHeight);
	charMask.SetAttrib(EAttFontTypeface);
	cl4->SetL(charFormat,charMask);
//	NOW DO IT
	test.Start(_L("Chain 4 layers deep, terminating on a based on NULL"));
	testCopyChain(*rChar,*cl1,0,(const CFormatLayer*)NULL);
	TInt restoredChainCount=rChar->ChainCount();
	test(ChainIsEqual(rChar,cl1));
//	DESTROY STUFF
	CCharFormatLayer* chCurrent=rChar;
	CCharFormatLayer* chNext=(CCharFormatLayer*)rChar->SenseBase();
	delete chCurrent;
	for (TInt loop=0;loop<restoredChainCount-1;loop++)	
		{
		chCurrent=chNext;
		chNext=(CCharFormatLayer*)chCurrent->SenseBase();
		delete chCurrent;
		}
	delete cl1;
	delete cl2;
	delete cl3;
	delete cl4;
	}
		

void DoCharChainVariant1()
//
// Case 2: Where the chain does not terminate at a NULL link.
//
	{
//	Create the chain of character format layers.
	CCharFormatLayer* cl1=CCharFormatLayer::NewL();
	CCharFormatLayer* cl2=CCharFormatLayer::NewL();
	CCharFormatLayer* cl3=CCharFormatLayer::NewL();
	CCharFormatLayer* cl4=CCharFormatLayer::NewL();
//	Chain together.
	cl1->SetBase(cl2);
	cl2->SetBase(cl3);
	cl3->SetBase(cl4);
//	Create head of restored format stream, and force it to get storage.	
	CCharFormatLayer* rChar=CCharFormatLayer::NewL();
	TCharFormat restoredCharFormat;
	TCharFormatMask restoredCharMask;
	restoredCharMask.ClearAll();
	rChar->SetL(restoredCharFormat,restoredCharMask);
//	General charformat and its mask.
	TCharFormat charFormat;	TCharFormatMask charMask;
	charMask.ClearAll();
//	Set layer one stuff
	charFormat.iFontSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold);	charMask.SetAttrib(EAttFontStrokeWeight);
	charFormat.iFontSpec.iFontStyle.SetPosture(EPostureItalic);	charMask.SetAttrib(EAttFontPosture);
	charFormat.iFontPresentation.iUnderline=EUnderlineOn;	charMask.SetAttrib(EAttFontUnderline);
	cl1->SetL(charFormat,charMask);
	charMask.ClearAll();
//	Set layer two stuff
	charFormat.iFontSpec.iFontStyle.SetPrintPosition(EPrintPosSubscript);	charMask.SetAttrib(EAttFontPrintPos);
	cl2->SetL(charFormat,charMask);
	charMask.ClearAll();
//	Set the layer 3 stuff.
	charFormat.iFontPresentation.iStrikethrough=EStrikethroughOn;	charMask.SetAttrib(EAttFontStrikethrough);
	cl3->SetL(charFormat,charMask);
	charMask.ClearAll();
//	Set the layer 4 stuff.
	charFormat.iFontSpec.iTypeface.iName=_L("Arial");
	charFormat.iFontSpec.iHeight=200;
	charMask.SetAttrib(EAttFontHeight);
	charMask.SetAttrib(EAttFontTypeface);
	cl4->SetL(charFormat,charMask);
//	NOW DO IT
	test.Next(_L("Chain 3 layers deep, terminating on a non-NULL based-on"));
	testCopyChain(*rChar,*cl1,1,(const CFormatLayer*)cl4);
	TInt restoredChainCount=rChar->ChainCount();
	test(ChainIsEqual(rChar,cl1));
//	DESTROY STUFF
	CCharFormatLayer* chCurrent=rChar;
	CCharFormatLayer* chNext=(CCharFormatLayer*)rChar->SenseBase();
	delete chCurrent;
	for (TInt loop=0;loop<restoredChainCount-2;loop++)	
		{
		chCurrent=chNext;
		chNext=(CCharFormatLayer*)chCurrent->SenseBase();
		delete chCurrent;
		}
	delete cl1;
	delete cl2;
	delete cl3;
	delete cl4;
	
	test.End();
	test.End();
	test.End();
	}


void testFmtLayerStoreChainL()
//
// Controls the testing of the chainig stuff.
//
	{
	DoParaChainL();
	DoCharChainL();
	DoCharChainVariant1();
//	DoCharChainVariant2();  TO BE IMPLEMENTED
//	doCharChainVariant3();	TO BE IMPLEMENTED
	}


void testFmtLayerL()
//
// Tests the streaming of format layers.
//
	{
	testFmtLayerStoreL();
	testFmtLayerStoreChainL();
	}


LOCAL_C void setupCleanup()
//
// Initialise the cleanup stack.
//
    {

	TheTrapCleanup=CTrapCleanup::New();
	TRAPD(r,\
		{\
		for (TInt i=KTestCleanupStack;i>0;i--)\
			CleanupStack::PushL((TAny*)1);\
		test(r==KErrNone);\
		CleanupStack::Pop(KTestCleanupStack);\
		});
	}


LOCAL_C void DeleteDataFile(const TDesC& aFullName)
	{
	RFs fsSession;
	TInt err = fsSession.Connect();
	if(err == KErrNone)
		{
		TEntry entry;
		if(fsSession.Entry(aFullName, entry) == KErrNone)
			{
			RDebug::Print(_L("Deleting \"%S\" file.\n"), &aFullName);
			err = fsSession.SetAtt(aFullName, 0, KEntryAttReadOnly);
			if(err != KErrNone) 
				{
				RDebug::Print(_L("Error %d changing \"%S\" file attributes.\n"), err, &aFullName);
				}
			err = fsSession.Delete(aFullName);
			if(err != KErrNone) 
				{
				RDebug::Print(_L("Error %d deleting \"%S\" file.\n"), err, &aFullName);
				}
			}
		fsSession.Close();
		}
	else
		{
		RDebug::Print(_L("Error %d connecting file session. File: %S.\n"), err, &aFullName);
		}
	}

GLDEF_C TInt E32Main()
//
// Test streaming conversions.
//
    {
	setupCleanup();
	test.Title();
	__UHEAP_MARK;
	
	test.Start(_L(" @SYMTestCaseID:SYSLIB-ETEXT-LEGACY-T_CONVS-0001 EText components using Flat Storage ")); 
	TRAPD(r,testPlainTextL(CEditableText::EFlatStorage));
	test(r==KErrNone);
	TRAP(r,testGlobalTextL(CEditableText::EFlatStorage));
	test(r==KErrNone);

//
	test.Next(_L("EText components using Segmented storage"));
	TRAP(r,testPlainTextL(CEditableText::ESegmentedStorage));
	test(r==KErrNone);
	TRAP(r,testGlobalTextL(CEditableText::ESegmentedStorage));
	test(r==KErrNone);

	test.Next(_L("Format Layer components"));
	TRAP(r,testFmtLayerL());
	test(r==KErrNone);
	
//	test.End();
	__UHEAP_MARKEND;
	
	::DeleteDataFile(KOutputFile);	//deletion of data files must be before call to End() - DEF047652
	test.End();
	test.Close();
	delete TheTrapCleanup;

	return 0;
    }

