diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btsdp/database/ExtractorVisitor.cpp --- /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 +#include "ExtractorVisitor.h" +#include +#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 (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 (2); + static_cast*>(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; (iCount()) && !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; (iCount()) && !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 + } +