bluetooth/btsdp/database/ExtractorVisitor.cpp
changeset 0 29b1cd4cb562
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btsdp/database/ExtractorVisitor.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,510 @@
+// Copyright (c) 2000-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 <bluetooth/logger.h>
+#include "ExtractorVisitor.h"
+#include <btsdp.h>
+#include "DataEncoder.h"
+#include "SDPDatabase.h"
+#include "sdputil.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_SDPDATABASE);
+#endif
+
+#ifdef _DEBUG
+PANICCATEGORY("extrvst");
+#endif
+
+CAttrExtractVisitor *CAttrExtractVisitor::NewLC(TElementEncoder aEncoder)
+	{
+	CAttrExtractVisitor *self = new(ELeave) CAttrExtractVisitor(aEncoder);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	return self;
+	}
+
+CAttrExtractVisitor *CAttrExtractVisitor::NewL(TElementEncoder aEncoder)
+	{
+	CAttrExtractVisitor *self = CAttrExtractVisitor::NewL(aEncoder);
+	CleanupStack::Pop();
+	return self;
+	}
+
+CAttrExtractVisitor::CAttrExtractVisitor(TElementEncoder aEncoder)
+	: iEncoder(aEncoder)
+	{
+	}
+
+void CAttrExtractVisitor::ConstructL()
+	{
+	}
+
+CAttrExtractVisitor::~CAttrExtractVisitor()
+	{
+	}
+
+// the only entry to this class - creates an instance of itself and then calls the FindAttributes
+// method of aList on the record supplied.
+EXPORT_C void CAttrExtractVisitor::EncodeAttributesL(TElementEncoder aEncoder, CSdpServRecord &aRec, CSdpAttrIdMatchList &aList)
+/**	Encode attributes in aRec that are in the match list aList, using aEncoder.*/
+	{
+	CAttrExtractVisitor *self = CAttrExtractVisitor::NewLC(aEncoder);
+
+	TRAPD(err, aList.FindAttributesL(aRec, *self));
+
+	// If we ran out of buffer space, a positive leave will have happened in VisitAttributeValueL, below
+	// else, the leave was an error, which should be re-thrown
+	if (err < 0)
+		User::Leave(err);
+
+	CleanupStack::PopAndDestroy(/*self*/);
+	}
+
+void CAttrExtractVisitor::AttributeInMatchListL(CSdpAttr &aAttr)
+	{
+	aAttr.AcceptVisitorL(*this);
+	}
+
+
+
+void CAttrExtractVisitor::VisitAttributeL(CSdpAttr &aAttribute)
+	{
+	//FIXME check rem
+	iEncoder.WriteUint(aAttribute.AttributeID(), sizeof(TSdpAttributeID));
+	}
+
+void CAttrExtractVisitor::VisitAttributeValueL(CSdpAttrValue &aValue, TSdpElementType aType)
+	{
+	TUint rem=0;
+	switch(aType)
+		{
+	case ETypeNil:
+		rem = iEncoder.WriteNil();
+		break;
+	case ETypeUint:
+	case ETypeInt:
+	case ETypeUUID:
+	case ETypeString:
+	case ETypeURL:
+		rem = iEncoder.WriteElement(aType, aValue.Des());
+		break;
+	case ETypeBoolean:
+		rem = iEncoder.WriteBoolean(aValue.Int());
+		break;
+	case ETypeDES:
+		rem = iEncoder.WriteDES(aValue.DataSize());
+		break;
+	case ETypeDEA:
+		rem = iEncoder.WriteDEA(aValue.DataSize());
+		break;
+	case ETypeEncoded:
+		rem= iEncoder.WriteDesc(aValue.Des());
+		break;
+		}
+	if (rem)
+		User::Leave(rem);
+	}
+
+void CAttrExtractVisitor::StartListL(CSdpAttrValueList &/*aList*/)
+	{
+	}
+
+void CAttrExtractVisitor::EndListL()
+	{
+	}
+
+
+
+
+//
+// CSdpAttrIdMatchList
+//
+
+CSdpAttrIdMatchList::CSdpAttrIdMatchList()
+	{
+	}
+
+EXPORT_C CSdpAttrIdMatchList* CSdpAttrIdMatchList::NewL()
+/** Allocate and construct a new CSdpAttrIdMatchList object.
+
+@return New CSdpAttrIdMatchList object */
+	{
+	CSdpAttrIdMatchList* self = new (ELeave) CSdpAttrIdMatchList;
+	CleanupStack::PushL(self);
+	self->iList = new (ELeave) CArrayFixFlat<TAttrRange> (2);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+EXPORT_C CSdpAttrIdMatchList* CSdpAttrIdMatchList::NewL(const CSdpAttrIdMatchList& aAttrMatchList)
+/** Allocate and construct a new CSdpAttrIdMatchList object as a copy of another 
+CSdpAttrIdMatchList.
+
+@param aAttrMatchList Object to copy
+@return New CSdpAttrIdMatchList object */
+	{
+	CSdpAttrIdMatchList* self = new (ELeave) CSdpAttrIdMatchList;
+	CleanupStack::PushL(self);
+	self->iList = new (ELeave) CArrayFixFlat<TAttrRange> (2);
+	static_cast<CArrayFixFlat<TAttrRange>*>(self->iList)->SetReserveL(aAttrMatchList.Count());
+	for(TInt i = 0; i < aAttrMatchList.Count(); ++i)
+		{
+		self->AddL((aAttrMatchList.iList)->At(i));
+		}
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+
+EXPORT_C CSdpAttrIdMatchList::~CSdpAttrIdMatchList()
+/** Destructor. */
+	{
+	delete iList;
+	}
+
+EXPORT_C void CSdpAttrIdMatchList::AddL(TAttrRange aRange)
+/** Adds a range of IDs to the list.
+
+@param aRange Range to add */
+	{
+	if (aRange.iStart > aRange.iEnd)
+		{// Bad range
+		User::Leave(KErrArgument);
+		}
+		
+		
+	// Look for insertion point (pos) in the match list. 
+		
+	TInt pos = 0; 	// this new item will start the list unless we find an existing
+					// range for it to overlap or follow
+
+	// If the start of the range 'aRange' is contiguous with one of the existing match list ranges
+	// then the insertion point is the index of that match list range.
+	// If not it is one more than the index of the match list range that
+	// precedes 'aRange'.
+	// N.B. The ranges are stored in ascending order
+
+	TBool bPositionFound = EFalse;
+	TBool bStartInRangeOrContiguous = EFalse;	
+
+	// Find the first position in the list in which (or contiguous with) this range lies
+	if (FindInOrContiguousWith(aRange.iStart, pos) == 0)
+		{
+		// the start of this new range overlaps with an existing one (returned in pos) or is contiguous with it
+		bPositionFound = ETrue;
+		bStartInRangeOrContiguous = ETrue;
+		}	
+	else
+		{
+		// find which range this new one will be after
+		for (TInt i=iList->Count()-1; i>=0 && !bPositionFound; i-- )
+			{
+			if (aRange.iStart > (iList->At(i).iEnd + 1 ))
+				{
+				pos = i+1;
+				bPositionFound = ETrue;
+				}
+			}
+		}
+
+	TAttrRange *insert;
+	
+	if (bStartInRangeOrContiguous)
+		{// Don't actually insert new entry, just grow the existing entry
+		insert = &iList->At(pos);
+		
+		// NB If the range to add has a start ID which is one less than the current start of range, 
+		// the range may grow "downwards", but will never join up with the preceding range because
+		// the start ID is known not to be contiguous with THAT range.
+		insert->iStart = Min(insert->iStart, aRange.iStart); 
+		insert->iEnd = Max(insert->iEnd, aRange.iEnd);
+		}
+	else
+		{// Add a new entry
+		iList->InsertL(pos, aRange);
+		insert = &iList->At(pos);
+		}
+
+	while (pos < iList->Count()-1 && insert->IsContiguousWith(iList->At(pos+1).iStart) )
+		{// There's now overlap with following item
+		insert->iEnd = Max(insert->iEnd, iList->At(pos+1).iEnd);
+		iList->Delete(pos+1);	// which leaves a new range to be checked at (pos+1), so no incrementing of pos is required	
+		}
+	__TEST_INVARIANT;
+	}
+
+EXPORT_C void CSdpAttrIdMatchList::RemoveL(TAttrRange aRange)
+/** Removes a range of IDs from the list.
+
+@param aRange Range to remove */
+	{
+	if (aRange.iStart > aRange.iEnd)
+		{// Bad range
+		User::Leave(KErrArgument);
+		}
+		
+	TInt pos = 0; // the deletion point	
+
+	TAttrRange *trim;
+	
+	// Find position for the start of the exclude range - is it partway through a current range?
+	if ((Find(aRange.iStart, pos) == 0) && (aRange.iStart > (&iList->At(pos))->iStart))
+		{
+		// Trim a bit off this entry
+		trim = &iList->At(pos);
+		
+		if (trim->iEnd > aRange.iEnd)
+			{
+			//It's bigger. Need to split in two
+			// Grow first, in case it fails
+			iList->InsertL(pos+1, TAttrRange(TSdpAttributeID(aRange.iEnd+1), trim->iEnd));
+			trim->iEnd = TSdpAttributeID(aRange.iStart - 1);
+			__TEST_INVARIANT;
+			return;	// We know this remove range is fully handled now
+			}
+		trim->iEnd = TSdpAttributeID(aRange.iStart - 1);
+		}
+		
+	// whether or not the trimming has already started, now move through the rest of the ranges	
+	// removing entire entries which lie within the deletion range.
+	while (pos < iList->Count() && (aRange.iEnd >= iList->At(pos).iEnd))
+		{
+		if (aRange.iStart <= iList->At(pos).iStart) 
+			{
+			// This complete item is not needed
+			iList->Delete(pos);		
+			}
+		else
+			{
+			// this item is not affected, because we have not yet got to the start of the exclude range
+			pos++; 
+			}	 
+		}
+
+	// Does the end of the deletion range lie within this entry?
+	if ( pos < iList->Count() )
+		{
+		trim = &iList->At(pos);
+		if ((trim->iStart >= aRange.iStart) && (trim->iStart <= aRange.iEnd))
+			{// The following item is too long -- trim it
+			trim->iStart = TSdpAttributeID(aRange.iEnd + 1);
+			}
+		}
+	__TEST_INVARIANT;
+	}
+
+TInt CSdpAttrIdMatchList::Find(TSdpAttributeID aAttrId, TInt &aPos) const
+/** Tests if the specified attribute ID is in the list, and gets its position.
+
+@param aAttrId Attribute ID to test
+@param aPos Position of ID in the list, or unchanged if attribute not found
+@return KErrNone if the attribute is in the list, else KErrNotFound */
+	{
+	__TEST_INVARIANT;
+	TAttrRange searchRange;
+	
+	TBool bAttribInRange = EFalse;
+	TInt ret=KErrNotFound;
+
+ 	for ( TInt i=0; (i<iList->Count()) && !bAttribInRange; i++ )
+ 		{
+ 		searchRange = iList->At(i);
+ 		if (searchRange.IsInRange(aAttrId) )
+ 			{
+ 			bAttribInRange = ETrue;
+ 			aPos = i;
+ 			ret = KErrNone;
+ 			}
+ 		}
+	return ret;	
+	}
+
+TInt CSdpAttrIdMatchList::FindInOrContiguousWith(TSdpAttributeID aAttrId, TInt &aPos) const
+/** Tests if the specified attribute ID is in the list (or next to a range in the list), and gets its position.
+
+@param aAttrId Attribute ID to test
+@param aPos Position in the list if the ID is found in a current range, or the lowest range with which the ID is contiguous, or unchanged if neither.
+@return KErrNone if the attribute is in the list, else KErrNotFound */
+	{
+	__TEST_INVARIANT;
+	TAttrRange searchRange;
+	
+	TBool bAttribInRange = EFalse;
+	TInt ret=KErrNotFound;
+
+ 	for ( TInt i=0; (i<iList->Count()) && !bAttribInRange; i++ )
+ 		{
+ 		searchRange = iList->At(i);
+ 		if (searchRange.IsContiguousWith(aAttrId) )
+ 			{
+ 			bAttribInRange = ETrue;
+ 			aPos = i;
+ 			ret = KErrNone;
+ 			}
+ 		}
+	return ret;	
+	}
+
+
+EXPORT_C TBool CSdpAttrIdMatchList::InMatchList(TSdpAttributeID aAttrId, TInt &aPos) const
+/** Tests if the specified attribute ID is in the list, and gets its position.
+	Do not use this - it gives an off-by-one error for aPos
+	unless the attribute is the first one in a contiguous range.
+	Use InMatchListRange() instead
+
+@param aAttrId Attribute ID to test
+@param aPos Position of ID in the list with an off-by-one error for IDs not at the start of a range, or 0 if not found
+@return ETrue if the attribute is in the list, else EFalse */
+	{
+	TKeyArrayFix key (_FOFF(TAttrRange, iStart), ECmpTUint16);
+	TAttrRange search(aAttrId);
+	TInt ret = iList->FindIsq(search, key, aPos);
+	return (ret == 0) || (aPos > 0 && iList->At(aPos-1).IsInRange(aAttrId));
+	}
+
+EXPORT_C TBool CSdpAttrIdMatchList::InMatchListRange(TSdpAttributeID aAttrId, TInt &aRange) const
+	{
+/* Tests if the specified attribute ID is in the list, and gets its position.
+
+Param aAttrId: Attribute ID to test
+Param aPos: Position of ID in the list, or 0 if not found
+Returns ETrue if the attribute is in the list, else EFalse */
+
+	
+	aRange = 0;	// will not be changed unless the attribute has a match in the list
+	TBool bAttribInRange = EFalse;
+	TInt ret = Find(aAttrId, aRange);
+	
+	if (ret == KErrNone)	
+		{
+		bAttribInRange = ETrue;  
+		}
+		
+	return bAttribInRange;
+	}
+
+EXPORT_C TUint CSdpAttrIdMatchList::EncodeL(TElementEncoder aEncoder) const
+/** Supplies encoder with attribute Ids in the match list
+
+@param aEncoder The encoder set to encode the attribute Ids in this match list
+@return Number of bytes of this CSdpAttrIdMatchList for which there was NO space in the encoder
+*/
+	{
+	TUint size = 0;
+	TInt i;
+	for (i = 0; i < iList->Count(); ++i)
+		{
+		TAttrRange& range = (*iList)[i];
+		if (range.iStart == range.iEnd)
+			size += 3; // 2 byte uint + header
+		else
+			size += 5; // 4 byte uint + header
+		}
+
+	TInt rem = aEncoder.WriteDES(size);
+	for (i = 0; i < iList->Count(); ++i)
+		{
+		TAttrRange& range = (*iList)[i];
+		if (range.iStart == range.iEnd)
+			{
+			rem += aEncoder.WriteUint(range.iStart, 2);
+			}
+		else
+			{
+			rem += aEncoder.WriteUint((range.iStart << 16) | range.iEnd, 4);
+			}
+		}	
+	return rem;
+	}
+
+
+EXPORT_C TBool CSdpAttrIdMatchList::InMatchList(TSdpAttributeID aAttrId) const
+	{
+	TInt pos;
+	return InMatchListRange(aAttrId, pos);
+	}
+
+EXPORT_C TInt CSdpAttrIdMatchList::Count() const
+	{
+	return iList->Count();
+	}
+	
+void CSdpAttrIdMatchList::FindAttributesL(CSdpServRecord &aRec, MAttributeMatchHandler &aHandler) const
+	{
+	for(TServAttrIter attrIter(aRec.AttributeIter()); attrIter; attrIter++)
+		{// Iterate thru attributes in record
+		if (InMatchList((*attrIter).AttributeID()))
+			{// this attribute is is the list -- signal it
+			aHandler.AttributeInMatchListL(*attrIter);
+			}
+		}
+	}
+
+MSdpElementBuilder* CSdpAttrIdMatchList::BuildUintL(const TDesC8& aUint)
+/** This should be one or two 16 bit integers, representing an AttrId of range thereof respectively. */
+	{
+	TInt len = aUint.Length();
+	if (len != 2 && len != 4)
+		{// Bad Uint size
+		User::Leave(KErrCorrupt);
+		}
+
+	// Get either the two or four byte version
+	TUint attrId = SdpUtil::GetUint(aUint);
+	TAttrRange range;
+	range.iStart = TUint16(len == 4 ? attrId >> 16 : attrId);
+	range.iEnd = TUint16(attrId);
+
+	// Add in place (optimised for incoming stream ordered -- but we will cope if isn't
+	// Will leave if a bad range is supplied
+	AddL(range);
+	return this;
+	}
+
+
+MSdpElementBuilder* CSdpAttrIdMatchList::BuildDESL()
+	{
+	// Could assert we only get one of these
+	return this;
+	}
+
+MSdpElementBuilder* CSdpAttrIdMatchList::StartListL()
+	{
+	return this;
+	}
+
+MSdpElementBuilder* CSdpAttrIdMatchList::EndListL()
+/**	Attribute list now complete. Stop the parser by returning 0. */
+	{
+	return 0;
+	}
+
+EXPORT_C void CSdpAttrIdMatchList::__DbgTestInvariant() const
+	{
+#ifdef _DEBUG
+	TInt count = iList->Count();
+	for (TInt i = 0; i < count; ++i)
+		{
+		TAttrRange& range = iList->At(i);
+		ASSERT_DEBUG(range.iStart <= range.iEnd);
+		if (i < count - 1)
+			{
+			ASSERT_DEBUG(range.iEnd + 1 < iList->At(i+1).iStart);
+			}
+		}
+#endif
+	}
+