libraries/ltkutils/src/bsymtree.cpp
author Tom Sutcliffe <thomas.sutcliffe@accenture.com>
Sat, 31 Jul 2010 19:07:57 +0100
changeset 23 092bcc217d9d
parent 0 7f656887cf89
permissions -rw-r--r--
Tidied iocli exports, build macro tweaks. Removed 4 overloads of CCommandBase::RunCommand[L] that are no longer used at all, and changed one more to not be exported as it's only used internally to iocli.dll. fixed builds on platforms that don't support btrace or any form of tracing.

// bsymtree.cpp
// 
// Copyright (c) 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "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:
// Accenture - Initial contribution
//
#include "bsymtree.h"
#include <badesca.h>
#include <fshell/descriptorutils.h>
//#include <e32debug.h>

using namespace LtkUtils;

RNode* RNode::NewL()
	{
	RNode* top = new(ELeave) RNode(0);
	CleanupDeletePushL(top);
	top->iPtr = new(ELeave) RArray<RNode*>(8);
	top->iHasChildArray = 1;
	CleanupStack::Pop(top);
	return top;
	}

RNode::RNode(char aLetter)
	: iLetter(aLetter), iHasChildArray(0), iPad(0), iPtr(NULL)
	{
	}

RNode* RNode::Sprog() const
	{
	ASSERT(!iHasChildArray);
	ASSERT(iLetter != 0);
	return reinterpret_cast<RNode*>(iPtr);
	}

RArray<RNode*>& RNode::Sprogs() const
	{
	ASSERT(iHasChildArray);
	return *reinterpret_cast<RArray<RNode*>*>(iPtr);
	}

RNode* RNode::ChildForLetter(char aChild) const
	{
	if (iHasChildArray)
		{
		const TInt count = Sprogs().Count();
		for (TInt i = 0; i < count; i++)
			{
			RNode* node = Sprogs()[i];
			if (node->iLetter == aChild) return node;
			}
		}
	else if (iLetter != 0) // null terminators use next for the value
		{
		RNode* child = Sprog();
		if (child && child->iLetter == aChild) return child;
		}
	return NULL;
	}

RNode* RNode::AddChildL(char aChild)
	{
	RNode* node = new(ELeave) RNode(aChild);
	CleanupStack::PushL(node);
	if (!iHasChildArray)
		{
		if (iPtr == NULL)
			{
			// Easy
			CleanupStack::Pop(node);
			iPtr = node;
			return node;
			}
		else
			{
			// Need to upgrade to a childArray
			RArray<RNode*>* children = new(ELeave) RArray<RNode*>(8);
			CleanupStack::PushL(children);
			children->AppendL(reinterpret_cast<RNode*>(iPtr));
			iPtr = children;
			CleanupStack::Pop(children);
			iHasChildArray = 1;
			}
		}
	Sprogs().AppendL(node);
	CleanupStack::Pop(node);
	return node;
	}

void RNode::InsertStringL(const TUint16* aString, TInt aValue)
	{
	char letter = (char)*aString;
	RNode* child = ChildForLetter(letter);
	if (!child)
		{
		child = AddChildL(letter);
		}
		
	if (letter == 0)
		{
		// If the letter was the null termination at the end, we're finished and just need to set the value
		//ASSERT(child->iPtr == NULL); // There should never be a child of a null terminator (unless the same string has been inserted twice...)
		// ^ Annoyingly there can be duplicate symbols that are only distinguished by information we've actually thrown away in the BSYM.
		//   We will ignore such duplicate symbols. If this is a problem, someone feel free to fix it.
		if (!child->iPtr)
			{
			child->iPtr = (TAny*)aValue;
			}
		}
	else
		{
		aString++;
		child->InsertStringL(aString, aValue);
		}
	}

RNode* RNode::WalkToEndOfString(TUint16*& aString)
	{
	RNode* currentNode = this;
	while (*aString)
		{
		RNode* child = currentNode->ChildForLetter((char)*aString);
		if (!child) return currentNode;
		currentNode = child;
		aString++;
		}
	return currentNode;
	}
	
RNode* RNode::TabFill(TUint16*& aString, const TUint16* aEnd)
	{
	// This function completes as far as the next choice point
	// aString is a pointer into a buffer with plenty of space in it
		
	//Dump(); // DEBUG
	if (!iHasChildArray && iLetter && iPtr && aString+1 < aEnd)
		{
		*aString = Sprog()->iLetter;
		aString++;
		*aString = 0;
		return Sprog()->TabFill(aString, aEnd);
		}
	else
		{
		return this;
		}
	}

void RNode::CompleteL(TDes& aPrefix, CDesC16Array& aResults)
	{
	TUint16* ptr = (TUint16*)aPrefix.PtrZ();
	TUint16* end = ptr + aPrefix.MaxLength();
	RNode* endNode = WalkToEndOfString(ptr);
	if (ptr != aPrefix.Ptr() + aPrefix.Length()) return; // We failed to walk the whole of the string so we have nothing to suggest (the string is longer than anything in the tree)

	endNode = endNode->TabFill(ptr, end);
	TInt newLen = ptr - aPrefix.Ptr();
	if (endNode->iLetter == 0 && newLen > 0) newLen--; // If ptr got as far as the null termination, we need to make our length one shorter
	//RDebug::Printf("Setting aPrefix len to %d, len=%d maxlen=%d", newLen, aPrefix.Length(), aPrefix.MaxLength());
	aPrefix.SetLength(newLen);

	// Now that aPrefix has been completed with as much as didn't have any choices, fill aResults with the choices
	RLtkBuf current;
	current.CreateLC(256);
	current.AppendL(aPrefix);
	if (current.Length()) current.SetLength(current.Length()-1); // Remove endNode's char as DoCompleteOptionsL expects to do that bit
	endNode->DoCompleteOptionsL(aResults, current);
	// Make sure last string is included
	aResults.AppendL(current);
	CleanupStack::PopAndDestroy(&current);
	aResults.Sort();
	}
	
void RNode::DoCompleteOptionsL(CDesC16Array& aResults, RLtkBuf& aCurrent)
	{
	if (iLetter)
		{
		aCurrent.ReserveExtraL(1);
		aCurrent.Append(TChar(iLetter));
		}
	TInt currentLen = aCurrent.Length();

	if (iHasChildArray)
		{
		for (int i = 0; i < Sprogs().Count(); i++)
			{
			RNode* child = Sprogs()[i];
			if (i > 0)
				{
				// A new prefix is in order
				aResults.AppendL(aCurrent);
				aCurrent.SetLength(currentLen);
				}
			child->DoCompleteOptionsL(aResults, aCurrent);
			}
		}
	else if (iLetter != 0)
		{
		if (Sprog()) Sprog()->DoCompleteOptionsL(aResults, aCurrent);
		}
	}

RNode::~RNode()
	{
	if (iHasChildArray)
		{
		RArray<RNode*>& children = Sprogs();
		const TInt count = children.Count();
		for (TInt i = 0; i < count; i++)
			{
			RNode* node = children[i];
			delete node;
			}
		children.Close();
		delete &children;
		}
	else if (iLetter != 0)
		{
		delete Sprog();
		}
	}

/*
#include <e32debug.h>
void RNode::Dump() const
	{
	TInt sprogCount = Sprogs().Count();
	for (int i = 0; i < sprogCount; i++)
		{
		RNode* child = Sprogs()[i];
		RDebug::Printf("Sprog %i is letter %c", i, child->iLetter);
		}
	}
*/

TInt RNode::ValueForStringL(const TDesC& aString) const
	{
	const RNode* node = this;
	for (TInt i = 0; i < aString.Length(); i++)
		{
		node = node->ChildForLetter(aString[i]);
		if (!node) User::Leave(KErrNotFound);
		}
	node = node->ChildForLetter(0); // The null terminator node is the one with the value in
	if (!node) User::Leave(KErrNotFound);
	ASSERT(!node->iHasChildArray);
	return reinterpret_cast<TInt>(node->iPtr);
	}