/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-file-style: "stroustrup" -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Robert John Churchill * Pierre Phaneuf * * * This Original Code has been modified by IBM Corporation. * Modifications made by IBM described herein are * Copyright (c) International Business Machines * Corporation, 2000 * * Modifications to Mozilla code or documentation * identified per MPL Section 3.3 * * Date Modified by Description of modification * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink * use in OS2 */ /* This file provides the implementation for the sort service manager. */ #include "nsCOMPtr.h" #include "nsCRT.h" #include "nsIAtom.h" #include "nsIContent.h" #include "nsIDOMElement.h" #include "nsIDOMNode.h" #include "nsIDocument.h" #include "nsINameSpaceManager.h" #include "nsIRDFContentModelBuilder.h" #include "nsIRDFCompositeDataSource.h" #include "nsIRDFNode.h" #include "nsIRDFObserver.h" #include "nsIRDFService.h" #include "nsIServiceManager.h" #include "nsINameSpaceManager.h" #include "nsIServiceManager.h" #include "nsISupportsArray.h" #include "nsIURL.h" #include "nsLayoutCID.h" #include "nsRDFCID.h" #include "nsIXULContentUtils.h" #include "nsString.h" #include "nsXPIDLString.h" #include "rdf.h" #include "rdfutil.h" #include "nsRDFSort.h" #include "nsVoidArray.h" #include "nsQuickSort.h" #include "nsIAtom.h" #include "nsIXULSortService.h" #include "nsString.h" #include "plhash.h" #include "plstr.h" #include "prlong.h" #include "prlog.h" #include "nsICollation.h" #include "nsCollationCID.h" #include "nsCOMPtr.h" #include "nsIServiceManager.h" #include "nsINameSpaceManager.h" #include "nsLayoutCID.h" #include "nsRDFCID.h" #include "nsIContent.h" #include "nsIDOMText.h" #include "nsVoidArray.h" #include "nsIDOMNode.h" #include "nsIDOMElement.h" #include "nsIRDFContentModelBuilder.h" #include "nsIRDFCompositeDataSource.h" #include "nsIRDFNode.h" #include "nsIRDFService.h" #include "rdf.h" #include "nsIDOMXULElement.h" #include "nsILocale.h" #include "nsLocaleCID.h" #include "nsILocaleService.h" #define TEST_SORT 1 //////////////////////////////////////////////////////////////////////// static NS_DEFINE_IID(kXULSortServiceCID, NS_XULSORTSERVICE_CID); static NS_DEFINE_IID(kIXULSortServiceIID, NS_IXULSORTSERVICE_IID); static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); static NS_DEFINE_CID(kNameSpaceManagerCID, NS_NAMESPACEMANAGER_CID); static NS_DEFINE_IID(kINameSpaceManagerIID, NS_INAMESPACEMANAGER_IID); static NS_DEFINE_IID(kIContentIID, NS_ICONTENT_IID); static NS_DEFINE_IID(kIDOMTextIID, NS_IDOMTEXT_IID); static NS_DEFINE_IID(kIDomNodeIID, NS_IDOMNODE_IID); static NS_DEFINE_IID(kIDomElementIID, NS_IDOMELEMENT_IID); static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); static NS_DEFINE_IID(kIRDFServiceIID, NS_IRDFSERVICE_IID); static NS_DEFINE_IID(kIRDFResourceIID, NS_IRDFRESOURCE_IID); static NS_DEFINE_IID(kIRDFLiteralIID, NS_IRDFLITERAL_IID); static NS_DEFINE_CID(kXULContentUtilsCID, NS_XULCONTENTUTILS_CID); static NS_DEFINE_IID(kIDomXulElementIID, NS_IDOMXULELEMENT_IID); static NS_DEFINE_CID(kCollationFactoryCID, NS_COLLATIONFACTORY_CID); static NS_DEFINE_IID(kICollationFactoryIID, NS_ICOLLATIONFACTORY_IID); static NS_DEFINE_CID(kRDFInMemoryDataSourceCID, NS_RDFINMEMORYDATASOURCE_CID); static NS_DEFINE_CID(kLocaleCID, NS_LOCALE_CID); static NS_DEFINE_IID(kILocaleIID, NS_ILOCALE_IID); // XXX This is sure to change. Copied from mozilla/layout/xul/content/src/nsXULAtoms.cpp static const char kXULNameSpaceURI[] = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; DEFINE_RDF_VOCAB(NC_NAMESPACE_URI, NC, Name); DEFINE_RDF_VOCAB(NC_NAMESPACE_URI, NC, BookmarkSeparator); typedef struct _sortStruct { PRBool firstFlag; nsCOMPtr sortProperty, sortProperty2; nsCOMPtr sortPropertyColl, sortPropertyColl2; nsCOMPtr sortPropertySort, sortPropertySort2; PRBool cacheFirstHint; nsCOMPtr cacheFirstNode; PRBool cacheIsFirstNodeCollationKey; nsCOMPtr db; nsCOMPtr rdfService; nsCOMPtr mInner; nsCOMPtr resCache; nsCOMPtr kTreeCellAtom; PRInt32 colIndex; nsCOMPtr parentContainer; PRInt32 kNameSpaceID_XUL; PRBool descendingSort; PRBool naturalOrderSort; PRBool inbetweenSeparatorSort; } sortStruct, *sortPtr; typedef struct { nsIContent * content; nsCOMPtr resource; nsCOMPtr collationNode1; nsCOMPtr collationNode2; nsCOMPtr sortNode1; nsCOMPtr sortNode2; nsCOMPtr node1; nsCOMPtr node2; PRBool checkedCollation1; PRBool checkedCollation2; PRBool checkedSort1; PRBool checkedSort2; PRBool checkedNode1; PRBool checkedNode2; } contentSortInfo; int PR_CALLBACK inplaceSortCallback(const void *data1, const void *data2, void *privateData); int testSortCallback(const void * data1, const void *data2, void *privateData); //////////////////////////////////////////////////////////////////////// // ServiceImpl // // This is the sort service. // class XULSortServiceImpl : public nsIXULSortService { protected: XULSortServiceImpl(void); virtual ~XULSortServiceImpl(void); static nsICollation *collationService; friend nsresult NS_NewXULSortService(nsIXULSortService** mgr); private: static nsrefcnt gRefCnt; static nsIAtom *kTreeAtom; static nsIAtom *kTreeCellAtom; static nsIAtom *kTreeChildrenAtom; static nsIAtom *kTreeColAtom; static nsIAtom *kTreeColGroupAtom; static nsIAtom *kTreeItemAtom; static nsIAtom *kContainerAtom; static nsIAtom *kResourceAtom; static nsIAtom *kResource2Atom; static nsIAtom *kSortActiveAtom; static nsIAtom *kSortResourceAtom; static nsIAtom *kSortResource2Atom; static nsIAtom *kSortDirectionAtom; static nsIAtom *kSortSeparatorsAtom; static nsIAtom *kRefAtom; static nsIAtom *kIdAtom; static nsIAtom *kRDF_type; static nsString trueStr; static nsString naturalStr; static nsString ascendingStr; static nsString descendingStr; static nsIRDFResource *kNC_Name; static nsIRDFResource *kRDF_instanceOf; static nsIRDFResource *kRDF_Seq; static PRInt32 kNameSpaceID_XUL; static PRInt32 kNameSpaceID_RDF; static nsIRDFService *gRDFService; static nsIXULContentUtils *gXULUtils; PRBool IsTreeElement(nsIContent *element); nsresult FindTreeElement(nsIContent *root, nsIContent* aElement,nsIContent** aTreeElement); nsresult FindTreeChildrenElement(nsIContent *tree, nsIContent **treeBody); nsresult GetSortColumnIndex(nsIContent *tree, const nsString &sortResource, const nsString& sortDirection, nsString &sortResource2, PRBool &inbetweenSeparatorSort, PRInt32 &colIndex, PRBool &found); nsresult SetSortHints(nsIContent *tree, const nsString &sortResource, const nsString &sortDirection, const nsString &sortResource2, PRBool inbetweenSeparatorSort, PRBool found); nsresult NodeHasSortInfo(nsIContent *node, nsString &sortResource, nsString &sortDirection, nsString &sortResource2, PRBool &inbetweenSeparatorSort, PRBool &found); nsresult GetSortColumnInfo(nsIContent *tree, nsString &sortResource, nsString &sortDirection, nsString &sortResource2, PRBool &inbetweenSeparatorSort); nsresult GetTreeCell(nsIContent *node, PRInt32 colIndex, nsIContent **cell); nsresult SortTreeChildren(nsIContent *container, sortPtr sortInfo); nsresult DoSort(nsIDOMNode* node, const nsString& sortResource, const nsString& sortDirection); static nsresult GetCachedTarget(sortPtr sortInfo, PRBool useCache, nsIRDFResource* aSource, nsIRDFResource *aProperty, PRBool aTruthValue, nsIRDFNode **aResult); static nsresult GetTarget(contentSortInfo *contentSortInfo, sortPtr sortInfo, PRBool first, PRBool onlyCollationHint, PRBool truthValue,nsIRDFNode **target, PRBool &isCollationKey); static nsresult GetResourceValue(nsIRDFResource *res1, sortPtr sortInfo, PRBool first, PRBool useCache, PRBool onlyCollationHint, nsIRDFNode **, PRBool &isCollationKey); static nsresult GetResourceValue(contentSortInfo *contentSortInfo, sortPtr sortInfo, PRBool first, PRBool useCache, PRBool onlyCollationHint, nsIRDFNode **target, PRBool &isCollationKey); static nsresult GetNodeValue(nsIContent *node1, sortPtr sortInfo, PRBool first, PRBool onlyCollationHint, nsIRDFNode **, PRBool &isCollationKey); static nsresult GetNodeValue(contentSortInfo *info1, sortPtr sortInfo, PRBool first, PRBool onlyCollationHint, nsIRDFNode **theNode, PRBool &isCollationKey); static nsresult GetTreeCell(sortPtr sortInfo, nsIContent *node, PRInt32 cellIndex, nsIContent **cell); static nsresult GetNodeTextValue(sortPtr sortInfo, nsIContent *node, nsString & val); public: static nsresult InplaceSort(nsIContent *node1, nsIContent *node2, sortPtr sortInfo, PRInt32 & sortOrder); static nsresult InplaceSort(contentSortInfo *info1, contentSortInfo *info2, sortPtr sortInfo, PRInt32 & sortOrder); static nsresult CompareNodes(nsIRDFNode *cellNode1, PRBool isCollationKey1, nsIRDFNode *cellNode2, PRBool isCollationKey2, PRBool &bothValid, PRInt32 & sortOrder); // nsISupports NS_DECL_ISUPPORTS // nsISortService NS_DECL_NSIXULSORTSERVICE }; nsICollation *XULSortServiceImpl::collationService = nsnull; nsIRDFService *XULSortServiceImpl::gRDFService = nsnull; nsrefcnt XULSortServiceImpl::gRefCnt = 0; nsIXULContentUtils *XULSortServiceImpl::gXULUtils = nsnull; nsIAtom* XULSortServiceImpl::kTreeAtom; nsIAtom* XULSortServiceImpl::kTreeCellAtom; nsIAtom* XULSortServiceImpl::kTreeChildrenAtom; nsIAtom* XULSortServiceImpl::kTreeColAtom; nsIAtom* XULSortServiceImpl::kTreeColGroupAtom; nsIAtom* XULSortServiceImpl::kTreeItemAtom; nsIAtom* XULSortServiceImpl::kContainerAtom; nsIAtom* XULSortServiceImpl::kResourceAtom; nsIAtom* XULSortServiceImpl::kResource2Atom; nsIAtom* XULSortServiceImpl::kSortActiveAtom; nsIAtom* XULSortServiceImpl::kSortResourceAtom; nsIAtom* XULSortServiceImpl::kSortResource2Atom; nsIAtom* XULSortServiceImpl::kSortDirectionAtom; nsIAtom* XULSortServiceImpl::kSortSeparatorsAtom; nsIAtom* XULSortServiceImpl::kRefAtom; nsIAtom* XULSortServiceImpl::kIdAtom; nsIAtom* XULSortServiceImpl::kRDF_type; nsString XULSortServiceImpl::trueStr; nsString XULSortServiceImpl::naturalStr; nsString XULSortServiceImpl::ascendingStr; nsString XULSortServiceImpl::descendingStr; nsIRDFResource *XULSortServiceImpl::kNC_Name; nsIRDFResource *XULSortServiceImpl::kRDF_instanceOf; nsIRDFResource *XULSortServiceImpl::kRDF_Seq; PRInt32 XULSortServiceImpl::kNameSpaceID_XUL; PRInt32 XULSortServiceImpl::kNameSpaceID_RDF; //////////////////////////////////////////////////////////////////////// XULSortServiceImpl::XULSortServiceImpl(void) { NS_INIT_REFCNT(); if (gRefCnt == 0) { kTreeAtom = NS_NewAtom("tree"); kTreeCellAtom = NS_NewAtom("treecell"); kTreeChildrenAtom = NS_NewAtom("treechildren"); kTreeColAtom = NS_NewAtom("treecol"); kTreeColGroupAtom = NS_NewAtom("treecolgroup"); kTreeItemAtom = NS_NewAtom("treeitem"); kContainerAtom = NS_NewAtom("container"); kResourceAtom = NS_NewAtom("resource"); kResource2Atom = NS_NewAtom("resource2"); kSortActiveAtom = NS_NewAtom("sortActive"); kSortResourceAtom = NS_NewAtom("sortResource"); kSortResource2Atom = NS_NewAtom("sortResource2"); kSortDirectionAtom = NS_NewAtom("sortDirection"); kSortSeparatorsAtom = NS_NewAtom("sortSeparators"); kRefAtom = NS_NewAtom("ref"); kIdAtom = NS_NewAtom("id"); kRDF_type = NS_NewAtom("type"); trueStr = "true"; naturalStr = "natural"; ascendingStr = "ascending"; descendingStr = "descending"; nsresult rv; if (NS_FAILED(rv = nsServiceManager::GetService(kRDFServiceCID, kIRDFServiceIID, (nsISupports**) &gRDFService))) { NS_ERROR("couldn't create rdf service"); } rv = nsServiceManager::GetService(kXULContentUtilsCID, NS_GET_IID(nsIXULContentUtils), (nsISupports**) &gXULUtils); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get XUL content utils"); // get a locale service nsCOMPtr localeService = do_GetService(NS_LOCALESERVICE_PROGID, &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr locale; if (NS_SUCCEEDED(rv = localeService->GetApplicationLocale(getter_AddRefs(locale))) && (locale)) { nsCOMPtr colFactory; if (NS_SUCCEEDED(rv = nsComponentManager::CreateInstance(kCollationFactoryCID, NULL, kICollationFactoryIID, getter_AddRefs(colFactory)))) { if (NS_FAILED(rv = colFactory->CreateCollation(locale, &collationService))) { NS_ERROR("couldn't create collation instance"); } } else { NS_ERROR("couldn't create instance of collation factory"); } } else { NS_ERROR("unable to get application locale"); } } else { NS_ERROR("couldn't get locale factory"); } gRDFService->GetResource(kURINC_Name, &kNC_Name); gRDFService->GetResource(RDF_NAMESPACE_URI "instanceOf", &kRDF_instanceOf); gRDFService->GetResource(RDF_NAMESPACE_URI "Seq", &kRDF_Seq); // Register the XUL and RDF namespaces: these'll just retrieve // the IDs if they've already been registered by someone else. nsINameSpaceManager* mgr; if (NS_SUCCEEDED(rv = nsComponentManager::CreateInstance(kNameSpaceManagerCID, nsnull, kINameSpaceManagerIID, (void**) &mgr))) { static const char kRDFNameSpaceURI[] = RDF_NAMESPACE_URI; rv = mgr->RegisterNameSpace(kXULNameSpaceURI, kNameSpaceID_XUL); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to register XUL namespace"); rv = mgr->RegisterNameSpace(kRDFNameSpaceURI, kNameSpaceID_RDF); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to register RDF namespace"); NS_RELEASE(mgr); } else { NS_ERROR("couldn't create namepsace manager"); } } ++gRefCnt; } XULSortServiceImpl::~XULSortServiceImpl(void) { #ifdef DEBUG_REFS --gInstanceCount; fprintf(stdout, "%d - RDF: XULSortServiceImpl\n", gInstanceCount); #endif --gRefCnt; if (gRefCnt == 0) { NS_IF_RELEASE(kTreeAtom); NS_IF_RELEASE(kTreeCellAtom); NS_IF_RELEASE(kTreeChildrenAtom); NS_IF_RELEASE(kTreeColAtom); NS_IF_RELEASE(kTreeColGroupAtom); NS_IF_RELEASE(kTreeItemAtom); NS_IF_RELEASE(kContainerAtom); NS_IF_RELEASE(kResourceAtom); NS_IF_RELEASE(kResource2Atom); NS_IF_RELEASE(kSortActiveAtom); NS_IF_RELEASE(kSortResourceAtom); NS_IF_RELEASE(kSortResource2Atom); NS_IF_RELEASE(kSortDirectionAtom); NS_IF_RELEASE(kSortSeparatorsAtom); NS_IF_RELEASE(kRefAtom); NS_IF_RELEASE(kIdAtom); NS_IF_RELEASE(kRDF_type); NS_IF_RELEASE(kNC_Name); NS_IF_RELEASE(kRDF_instanceOf); NS_IF_RELEASE(kRDF_Seq); NS_IF_RELEASE(collationService); collationService = nsnull; if (gRDFService) { nsServiceManager::ReleaseService(kRDFServiceCID, gRDFService); gRDFService = nsnull; } if (gXULUtils) { nsServiceManager::ReleaseService(kXULContentUtilsCID, gXULUtils); gXULUtils = nsnull; } } } NS_IMPL_ISUPPORTS(XULSortServiceImpl, NS_GET_IID(nsIXULSortService)); //////////////////////////////////////////////////////////////////////// PRBool XULSortServiceImpl::IsTreeElement(nsIContent *element) { PRBool isTreeNode = PR_FALSE; PRInt32 nameSpaceID; nsresult rv; if (NS_SUCCEEDED(rv = element->GetNameSpaceID(nameSpaceID))) { if (nameSpaceID == kNameSpaceID_XUL) { nsCOMPtr tag; if (NS_SUCCEEDED(rv = element->GetTag(*getter_AddRefs(tag)))) { if (tag.get() == kTreeAtom) { isTreeNode = PR_TRUE; } } } } return(isTreeNode); } nsresult XULSortServiceImpl::FindTreeElement(nsIContent *root, nsIContent *aElement, nsIContent **aTreeElement) { nsresult rv; *aTreeElement = nsnull; if (root) { // we have a root hint, so look under it for the tree tag PRInt32 numKids, loop; if (NS_SUCCEEDED(rv = root->ChildCount(numKids))) { for (loop=0; loop child; if (NS_FAILED(rv = root->ChildAt(loop, *getter_AddRefs(child)))) return(rv); if (IsTreeElement(child) == PR_TRUE) { *aTreeElement = child; NS_ADDREF(*aTreeElement); return(NS_OK); } } } } else { // we don't have a root hint, so look from the current // node upwards until we find it (or hit the top) nsCOMPtr element(do_QueryInterface(aElement)); while (element) { if (IsTreeElement(element) == PR_TRUE) { *aTreeElement = element; NS_ADDREF(*aTreeElement); return(NS_OK); } nsCOMPtr parent; element->GetParent(*getter_AddRefs(parent)); element = parent; } } return(NS_ERROR_FAILURE); } nsresult XULSortServiceImpl::FindTreeChildrenElement(nsIContent *tree, nsIContent **treeBody) { nsCOMPtr child; PRInt32 childIndex = 0, numChildren = 0, nameSpaceID; nsresult rv; if (NS_FAILED(rv = tree->ChildCount(numChildren))) return(rv); for (childIndex=0; childIndexChildAt(childIndex, *getter_AddRefs(child)))) return(rv); if (NS_FAILED(rv = child->GetNameSpaceID(nameSpaceID))) return rv; if (nameSpaceID == kNameSpaceID_XUL) { nsCOMPtr tag; if (NS_FAILED(rv = child->GetTag(*getter_AddRefs(tag)))) return rv; if (tag.get() == kTreeChildrenAtom) { *treeBody = child; NS_ADDREF(*treeBody); return NS_OK; } } } return(NS_ERROR_FAILURE); } nsresult XULSortServiceImpl::GetSortColumnIndex(nsIContent *tree, const nsString &sortResource, const nsString &sortDirection, nsString &sortResource2, PRBool &inbetweenSeparatorSort, PRInt32 &sortColIndex, PRBool &found) { PRInt32 childIndex, colIndex = 0, numChildren, nameSpaceID; nsCOMPtr child; nsresult rv; found = PR_FALSE; inbetweenSeparatorSort = PR_FALSE; sortColIndex = 0; if (NS_FAILED(rv = tree->ChildCount(numChildren))) return(rv); for (childIndex=0; childIndexChildAt(childIndex, *getter_AddRefs(child)))) return(rv); if (NS_FAILED(rv = child->GetNameSpaceID(nameSpaceID))) return(rv); if (nameSpaceID == kNameSpaceID_XUL) { nsCOMPtr tag; if (NS_FAILED(rv = child->GetTag(*getter_AddRefs(tag)))) return(rv); if (tag.get() == kTreeColGroupAtom) { rv = GetSortColumnIndex(child, sortResource, sortDirection, sortResource2, inbetweenSeparatorSort, sortColIndex, found); } else if (tag.get() == kTreeColAtom) { nsAutoString value; if (NS_SUCCEEDED(rv = child->GetAttribute(kNameSpaceID_RDF, kResourceAtom, value)) && (rv == NS_CONTENT_ATTR_HAS_VALUE)) { PRBool setFlag = PR_FALSE; if (value == sortResource) { sortColIndex = colIndex; if (!sortDirection.Equals(naturalStr)) { found = PR_TRUE; setFlag = PR_TRUE; // secondary sort info is optional if (NS_FAILED(rv = child->GetAttribute(kNameSpaceID_RDF, kResource2Atom, sortResource2)) || (rv != NS_CONTENT_ATTR_HAS_VALUE)) { sortResource2.Truncate(); } } } if (NS_SUCCEEDED(rv = child->GetAttribute(kNameSpaceID_None, kSortSeparatorsAtom, value)) && (rv == NS_CONTENT_ATTR_HAS_VALUE) && (value.EqualsIgnoreCase(trueStr))) { inbetweenSeparatorSort = PR_TRUE; } if (setFlag == PR_TRUE) { child->SetAttribute(kNameSpaceID_None, kSortActiveAtom, trueStr, PR_TRUE); child->SetAttribute(kNameSpaceID_None, kSortDirectionAtom, sortDirection, PR_TRUE); // Note: don't break out of loop; want to set/unset attribs on ALL sort columns // break; } else { child->UnsetAttribute(kNameSpaceID_None, kSortActiveAtom, PR_TRUE); child->UnsetAttribute(kNameSpaceID_None, kSortDirectionAtom, PR_TRUE); } } ++colIndex; } } } SetSortHints(tree, sortResource, sortDirection, sortResource2, inbetweenSeparatorSort, found); return(NS_OK); } nsresult XULSortServiceImpl::SetSortHints(nsIContent *tree, const nsString &sortResource, const nsString &sortDirection, const nsString &sortResource2, PRBool inbetweenSeparatorSort, PRBool found) { if (found == PR_TRUE) { // set hints on tree root node tree->SetAttribute(kNameSpaceID_None, kSortActiveAtom, trueStr, PR_FALSE); tree->SetAttribute(kNameSpaceID_None, kSortDirectionAtom, sortDirection, PR_FALSE); tree->SetAttribute(kNameSpaceID_RDF, kResourceAtom, sortResource, PR_FALSE); if (sortResource2.Length() > 0) tree->SetAttribute(kNameSpaceID_RDF, kResource2Atom, sortResource2, PR_FALSE); else tree->UnsetAttribute(kNameSpaceID_RDF, kResource2Atom, PR_FALSE); } else { tree->UnsetAttribute(kNameSpaceID_None, kSortActiveAtom, PR_FALSE); tree->UnsetAttribute(kNameSpaceID_None, kSortDirectionAtom, PR_FALSE); tree->UnsetAttribute(kNameSpaceID_RDF, kResourceAtom, PR_FALSE); tree->UnsetAttribute(kNameSpaceID_RDF, kResource2Atom, PR_FALSE); } // optional hint if (inbetweenSeparatorSort == PR_TRUE) tree->SetAttribute(kNameSpaceID_None, kSortSeparatorsAtom, trueStr, PR_FALSE); else tree->UnsetAttribute(kNameSpaceID_None, kSortSeparatorsAtom, PR_FALSE); return(NS_OK); } nsresult XULSortServiceImpl::NodeHasSortInfo(nsIContent *child, nsString &sortResource, nsString &sortDirection, nsString &sortResource2, PRBool &inbetweenSeparatorSort, PRBool &found) { nsresult rv; inbetweenSeparatorSort = PR_FALSE; found = PR_FALSE; nsAutoString value; if (NS_SUCCEEDED(rv = child->GetAttribute(kNameSpaceID_None, kSortActiveAtom, value)) && (rv == NS_CONTENT_ATTR_HAS_VALUE)) { if (value.EqualsIgnoreCase(trueStr)) { if (NS_SUCCEEDED(rv = child->GetAttribute(kNameSpaceID_RDF, kResourceAtom, sortResource)) && (rv == NS_CONTENT_ATTR_HAS_VALUE)) { if (NS_SUCCEEDED(rv = child->GetAttribute(kNameSpaceID_None, kSortDirectionAtom, sortDirection)) && (rv == NS_CONTENT_ATTR_HAS_VALUE)) { found = PR_TRUE; // sort separator flag is optional if (NS_SUCCEEDED(rv = child->GetAttribute(kNameSpaceID_None, kSortSeparatorsAtom, value)) && (rv == NS_CONTENT_ATTR_HAS_VALUE)) { if (value.EqualsIgnoreCase(trueStr)) { inbetweenSeparatorSort = PR_TRUE; } } // secondary sort info is optional if (NS_FAILED(rv = child->GetAttribute(kNameSpaceID_RDF, kResource2Atom, sortResource2)) || (rv != NS_CONTENT_ATTR_HAS_VALUE)) { sortResource2.Truncate(); } } } } } return(NS_OK); } nsresult XULSortServiceImpl::GetSortColumnInfo(nsIContent *tree, nsString &sortResource, nsString &sortDirection, nsString &sortResource2, PRBool &inbetweenSeparatorSort) { nsCOMPtr child; PRBool found = PR_FALSE; PRInt32 childIndex, nameSpaceID, numChildren; nsresult rv; if (IsTreeElement(tree) && (NS_SUCCEEDED(rv = NodeHasSortInfo(tree, sortResource, sortDirection, sortResource2, inbetweenSeparatorSort, found)) && (found == PR_TRUE))) { } else { if (NS_FAILED(rv = tree->ChildCount(numChildren))) return(rv); for (childIndex=0; childIndexChildAt(childIndex, *getter_AddRefs(child)))) return(rv); if (NS_FAILED(rv = child->GetNameSpaceID(nameSpaceID))) return(rv); if (nameSpaceID != kNameSpaceID_XUL) continue; nsCOMPtr tag; if (NS_FAILED(rv = child->GetTag(*getter_AddRefs(tag)))) return(rv); if (tag.get() == kTreeColGroupAtom) { if (NS_SUCCEEDED(rv = GetSortColumnInfo(child, sortResource, sortDirection, sortResource2, inbetweenSeparatorSort))) { break; } } else if (tag.get() == kTreeColAtom) { if (NS_SUCCEEDED(rv = NodeHasSortInfo(child, sortResource, sortDirection, sortResource2, inbetweenSeparatorSort, found)) && (found == PR_TRUE)) { break; } } else { continue; } } SetSortHints(tree, sortResource, sortDirection, sortResource2, inbetweenSeparatorSort, found); } return((found == PR_TRUE) ? NS_OK : NS_ERROR_FAILURE); } nsresult XULSortServiceImpl::GetTreeCell(sortPtr sortInfo, nsIContent *node, PRInt32 cellIndex, nsIContent **cell) { PRBool found = PR_FALSE; PRInt32 childIndex = 0, numChildren = 0, nameSpaceID; nsCOMPtr child; nsresult rv; if (NS_FAILED(rv = node->ChildCount(numChildren))) return(rv); for (childIndex=0; childIndexChildAt(childIndex, *getter_AddRefs(child)))) break; if (NS_FAILED(rv = child->GetNameSpaceID(nameSpaceID))) break; if (nameSpaceID == sortInfo->kNameSpaceID_XUL) { nsCOMPtr tag; if (NS_FAILED(rv = child->GetTag(*getter_AddRefs(tag)))) return rv; if (tag.get() == sortInfo->kTreeCellAtom.get()) { if (cellIndex == 0) { found = PR_TRUE; *cell = child; NS_ADDREF(*cell); break; } --cellIndex; } } } return((found == PR_TRUE) ? NS_OK : NS_ERROR_FAILURE); } nsresult XULSortServiceImpl::GetNodeTextValue(sortPtr sortInfo, nsIContent *node, nsString & val) { PRBool found = PR_FALSE; PRInt32 childIndex = 0, numChildren = 0, nameSpaceID; nsCOMPtr child; nsresult rv; if (NS_FAILED(rv = node->ChildCount(numChildren))) return(rv); for (childIndex=0; childIndexChildAt(childIndex, *getter_AddRefs(child)))) break; if (NS_FAILED(rv = child->GetNameSpaceID(nameSpaceID))) break; if (nameSpaceID != sortInfo->kNameSpaceID_XUL) { // Get text using the DOM nsCOMPtr domText; rv = child->QueryInterface(kIDOMTextIID, getter_AddRefs(domText)); if (NS_FAILED(rv)) break; val.Truncate(); domText->GetData(val); found = PR_TRUE; break; } } return((found == PR_TRUE) ? NS_OK : NS_ERROR_FAILURE); } nsresult XULSortServiceImpl::CompareNodes(nsIRDFNode *cellNode1, PRBool isCollationKey1, nsIRDFNode *cellNode2, PRBool isCollationKey2, PRBool &bothValid, PRInt32 & sortOrder) { bothValid = PR_FALSE; sortOrder = 0; const PRUnichar *uni1 = nsnull, *uni2 = nsnull; nsCOMPtr literal1 = do_QueryInterface(cellNode1); nsCOMPtr literal2 = do_QueryInterface(cellNode2); if (literal1) literal1->GetValueConst(&uni1); if (literal2) literal2->GetValueConst(&uni2); if (isCollationKey1 == PR_TRUE && isCollationKey2 == PR_TRUE) { bothValid = PR_TRUE; // sort collation keys if (collationService) { nsAutoString uni1Str(uni1); nsAutoString uni2Str(uni2); collationService->CompareSortKey(uni1Str, uni2Str, &sortOrder); } else { // without a collation service, unable to collate sortOrder = 0; } } else if ((isCollationKey1 == PR_TRUE) && (isCollationKey2 == PR_FALSE)) { sortOrder = -1; } else if ((isCollationKey1 == PR_FALSE) && (isCollationKey2 == PR_TRUE)) { sortOrder = 1; } else if (literal1 || literal2) { // not a collation key, but one or both are strings if (literal1 && literal2) { if ((*uni1) && (*uni2)) { bothValid = PR_TRUE; sortOrder = nsCRT::strcasecmp(uni1, uni2); } else if (*uni1) sortOrder = -1; else sortOrder = 1; } else if (literal1) sortOrder = -1; else sortOrder = 1; } else { // not a collation key, and both aren't strings, so try other data types (ints) nsCOMPtr intLiteral1 = do_QueryInterface(cellNode1); nsCOMPtr intLiteral2 = do_QueryInterface(cellNode2); if (intLiteral1 && intLiteral2) { PRInt32 intVal1, intVal2; intLiteral1->GetValue(&intVal1); intLiteral2->GetValue(&intVal2); bothValid = PR_TRUE; sortOrder = 0; if (intVal1 < intVal2) sortOrder = -1; else if (intVal1 > intVal2) sortOrder = 1; } else { // not a collation key, and both aren't strings, so try other data types (dates) nsCOMPtr dateLiteral1 = do_QueryInterface(cellNode1); nsCOMPtr dateLiteral2 = do_QueryInterface(cellNode2); sortOrder = 0; if (dateLiteral1 && dateLiteral2) { PRInt64 dateVal1, dateVal2; dateLiteral1->GetValue(&dateVal1); dateLiteral2->GetValue(&dateVal2); bothValid = PR_TRUE; if (LL_CMP(dateVal1, <, dateVal2)) sortOrder = -1; else if (LL_CMP(dateVal1, >, dateVal2)) sortOrder = 1; } else if (dateLiteral1) sortOrder = -1; else if (dateLiteral2) sortOrder = 1; } } return(NS_OK); } nsresult XULSortServiceImpl::GetCachedTarget(sortPtr sortInfo, PRBool useCache, nsIRDFResource* aSource, nsIRDFResource *aProperty, PRBool aTruthValue, nsIRDFNode **aResult) { nsresult rv; *aResult = nsnull; if (!(sortInfo->mInner)) { // if we don't have a mInner, create one rv = nsComponentManager::CreateInstance(kRDFInMemoryDataSourceCID, nsnull, NS_GET_IID(nsIRDFDataSource), (void **)&(sortInfo->mInner)); if (NS_FAILED(rv)) return(rv); } rv = NS_RDF_NO_VALUE; if (sortInfo->mInner) { if (useCache == PR_TRUE) { // if we do have a mInner, look for the resource in it rv = sortInfo->mInner->GetTarget(aSource, aProperty, aTruthValue, aResult); } else if (sortInfo->db) { // if we don't have a cached value, look it up in the document's DB if (NS_SUCCEEDED(rv = (sortInfo->db)->GetTarget(aSource, aProperty, aTruthValue, aResult)) && (rv != NS_RDF_NO_VALUE)) { // and if we have a value, cache it away in our mInner also (ignore errors) sortInfo->mInner->Assert(aSource, aProperty, *aResult, PR_TRUE); } } } return(rv); } nsresult XULSortServiceImpl::GetResourceValue(nsIRDFResource *res1, sortPtr sortInfo, PRBool first, PRBool useCache, PRBool onlyCollationHint, nsIRDFNode **target, PRBool &isCollationKey) { nsresult rv = NS_OK; *target = nsnull; isCollationKey = PR_FALSE; if ((res1) && (sortInfo->naturalOrderSort == PR_FALSE)) { nsCOMPtr modSortRes; // for any given property, first ask the graph for its value with "?collation=true" appended // to indicate that if there is a collation key available for this value, we want it modSortRes = (first) ? sortInfo->sortPropertyColl : sortInfo->sortPropertyColl2; if (modSortRes) { if (NS_SUCCEEDED(rv = GetCachedTarget(sortInfo, useCache, res1, modSortRes, PR_TRUE, target)) && (rv != NS_RDF_NO_VALUE)) { isCollationKey = PR_TRUE; } } if ((!(*target)) && (onlyCollationHint == PR_FALSE)) { // if no collation key, ask the graph for its value with "?sort=true" appended // to indicate that if there is any distinction between its display value and sorting // value, we want the sorting value (so that, for example, a mail datasource could strip // off a "Re:" on a mail message subject) modSortRes = (first) ? sortInfo->sortPropertySort : sortInfo->sortPropertySort2; if (modSortRes) { if (NS_SUCCEEDED(rv = GetCachedTarget(sortInfo, useCache, res1, modSortRes, PR_TRUE, target)) && (rv != NS_RDF_NO_VALUE)) { } } } if ((!(*target)) && (onlyCollationHint == PR_FALSE)) { // if no collation key and no special sorting value, just get the property value modSortRes = (first) ? sortInfo->sortProperty : sortInfo->sortProperty2; if (modSortRes) { if (NS_SUCCEEDED(rv = GetCachedTarget(sortInfo, useCache, res1, modSortRes, PR_TRUE, target) && (rv != NS_RDF_NO_VALUE))) { } } } } return(rv); } nsresult XULSortServiceImpl::GetTarget(contentSortInfo *contentSortInfo, sortPtr sortInfo, PRBool first, PRBool onlyCollationHint, PRBool truthValue, nsIRDFNode **target, PRBool &isCollationKey) { nsresult rv; nsIRDFResource *resource = contentSortInfo->resource; if(first) { if(contentSortInfo->collationNode1) { *target = contentSortInfo->collationNode1; NS_IF_ADDREF(*target); } else if ( !contentSortInfo->checkedCollation1 && NS_SUCCEEDED(rv = (sortInfo->db)->GetTarget(resource, sortInfo->sortPropertyColl, truthValue, target))) { if(rv != NS_RDF_NO_VALUE) contentSortInfo->collationNode1 = *target; contentSortInfo->checkedCollation1 = PR_TRUE; } if(*target) { isCollationKey = PR_TRUE; return NS_OK; } if(onlyCollationHint == PR_FALSE) { if(contentSortInfo->sortNode1) { *target = contentSortInfo->sortNode1; NS_IF_ADDREF(*target); } else if (!contentSortInfo->checkedSort1 && NS_SUCCEEDED(rv = (sortInfo->db)->GetTarget(resource, sortInfo->sortPropertySort, truthValue, target))) { if(rv != NS_RDF_NO_VALUE) contentSortInfo->sortNode1 = *target; contentSortInfo->checkedSort1 = PR_TRUE; } if(*target) return NS_OK; if(contentSortInfo->node1) { *target = contentSortInfo->node1; NS_IF_ADDREF(*target); } else if (!contentSortInfo->checkedNode1 && NS_SUCCEEDED(rv = (sortInfo->db)->GetTarget(resource, sortInfo->sortProperty, truthValue, target))) { if(rv != NS_RDF_NO_VALUE) contentSortInfo->node1 = *target; contentSortInfo->checkedNode1 = PR_TRUE; } if(*target) return NS_OK; } } else { if(contentSortInfo->collationNode2) { *target = contentSortInfo->collationNode2; NS_IF_ADDREF(*target); } else if (!contentSortInfo->checkedCollation2 && NS_SUCCEEDED(rv = (sortInfo->db)->GetTarget(resource, sortInfo->sortPropertyColl2, truthValue, target))) { if(rv != NS_RDF_NO_VALUE) contentSortInfo->collationNode2 = *target; contentSortInfo->checkedCollation2 = PR_TRUE; } if(*target) { isCollationKey = PR_TRUE; return NS_OK; } if(onlyCollationHint == PR_FALSE) { if(contentSortInfo->sortNode2) { *target = contentSortInfo->sortNode2; NS_IF_ADDREF(*target); } else if (!contentSortInfo->checkedSort2 && NS_SUCCEEDED(rv = (sortInfo->db)->GetTarget(resource, sortInfo->sortPropertySort2, truthValue, target))) { if(rv != NS_RDF_NO_VALUE) contentSortInfo->sortNode2 = *target; contentSortInfo->checkedSort2 = PR_TRUE; } if(*target) return NS_OK; if(contentSortInfo->node2) { *target = contentSortInfo->node2; NS_IF_ADDREF(*target); } else if (!contentSortInfo->checkedNode2 && NS_SUCCEEDED(rv = (sortInfo->db)->GetTarget(resource, sortInfo->sortProperty2, truthValue, target))) { if(rv != NS_RDF_NO_VALUE) contentSortInfo->node2 = *target; contentSortInfo->checkedNode2 = PR_TRUE; } if(*target) return NS_OK; } } return NS_RDF_NO_VALUE; } nsresult XULSortServiceImpl::GetResourceValue(contentSortInfo *contentSortInfo, sortPtr sortInfo, PRBool first, PRBool useCache, PRBool onlyCollationHint, nsIRDFNode **target, PRBool &isCollationKey) { nsresult rv = NS_OK; *target = nsnull; isCollationKey = PR_FALSE; nsIRDFResource *res1 = contentSortInfo->resource; if ((res1) && (sortInfo->naturalOrderSort == PR_FALSE)) { rv = GetTarget(contentSortInfo, sortInfo, first, onlyCollationHint, PR_TRUE, target, isCollationKey); } return(rv); } nsresult XULSortServiceImpl::GetNodeValue(nsIContent *node1, sortPtr sortInfo, PRBool first, PRBool onlyCollationHint, nsIRDFNode **theNode, PRBool &isCollationKey) { nsresult rv; nsCOMPtr res1; *theNode = nsnull; isCollationKey = PR_FALSE; nsCOMPtr dom1 = do_QueryInterface(node1); if (dom1) { if (NS_FAILED(rv = dom1->GetResource(getter_AddRefs(res1)))) { res1 = nsnull; } // Note: don't check for res1 QI failure here. It only succeeds for RDF nodes, // but for XUL nodes it will failure; in the failure case, the code below gets // the cell's text value straight from the DOM } else { nsCOMPtr htmlDom = do_QueryInterface(node1); if (htmlDom) { nsAutoString htmlID; if (NS_SUCCEEDED(rv = node1->GetAttribute(kNameSpaceID_None, kIdAtom, htmlID)) && (rv == NS_CONTENT_ATTR_HAS_VALUE)) { if (NS_FAILED(rv = gRDFService->GetUnicodeResource(htmlID.GetUnicode(), getter_AddRefs(res1)))) { res1 = nsnull; } } } else { return(NS_ERROR_FAILURE); } } if ((sortInfo->naturalOrderSort == PR_FALSE) && (sortInfo->sortProperty)) { if (res1) { rv = GetResourceValue(res1, sortInfo, first, PR_TRUE, onlyCollationHint, theNode, isCollationKey); if ((rv == NS_RDF_NO_VALUE) || (!*theNode)) { rv = GetResourceValue(res1, sortInfo, first, PR_FALSE, onlyCollationHint, theNode, isCollationKey); } } else { rv = NS_RDF_NO_VALUE; } if ((onlyCollationHint == PR_FALSE) && (NS_FAILED(rv) || (rv == NS_RDF_NO_VALUE))) { nsCOMPtr cell1; if (sortInfo->colIndex >= 0) { rv = GetTreeCell(sortInfo, node1, sortInfo->colIndex, getter_AddRefs(cell1)); } else { cell1 = node1; } if (cell1) { nsAutoString cellVal1; if (NS_SUCCEEDED(rv = GetNodeTextValue(sortInfo, cell1, cellVal1)) && (rv != NS_RDF_NO_VALUE)) { nsCOMPtr nodeLiteral; rv = gRDFService->GetLiteral(cellVal1.GetUnicode(), getter_AddRefs(nodeLiteral)); if (NS_SUCCEEDED(rv)) { *theNode = nodeLiteral; NS_IF_ADDREF(*theNode); isCollationKey = PR_FALSE; } } } } } else if ((sortInfo->naturalOrderSort == PR_TRUE) && (sortInfo->parentContainer)) { nsAutoString cellPosVal1; // check to see if this is a RDF_Seq // Note: this code doesn't handle the aggregated Seq case especially well if ((res1) && (sortInfo->db)) { nsCOMPtr parentResource; nsCOMPtr parentDOMNode = do_QueryInterface(sortInfo->parentContainer); if (parentDOMNode) { if (NS_FAILED(rv = parentDOMNode->GetResource(getter_AddRefs(parentResource)))) { parentResource = nsnull; } } nsCOMPtr arcs; if ((parentResource) && (NS_SUCCEEDED(rv = sortInfo->db->ArcLabelsIn(res1, getter_AddRefs(arcs))))) { PRBool hasMore = PR_TRUE; while(hasMore) { if (NS_FAILED(rv = arcs->HasMoreElements(&hasMore))) break; if (hasMore == PR_FALSE) break; nsCOMPtr isupports; if (NS_FAILED(rv = arcs->GetNext(getter_AddRefs(isupports)))) break; nsCOMPtr property = do_QueryInterface(isupports); if (!property) continue; // hack: it it looks like a RDF_Seq and smells like a RDF_Seq... static const char kRDFNameSpace_Seq_Prefix[] = "http://www.w3.org/1999/02/22-rdf-syntax-ns#_"; const char *uri = nsnull; if (NS_FAILED(rv = property->GetValueConst(&uri))) continue; if (!uri) continue; if (nsCRT::strncasecmp(uri, kRDFNameSpace_Seq_Prefix, sizeof(kRDFNameSpace_Seq_Prefix)-1)) continue; nsCOMPtr srcs; if (NS_FAILED(rv = sortInfo->db->GetSources(property, res1, PR_TRUE, getter_AddRefs(srcs)))) continue; PRBool hasMoreSrcs = PR_TRUE; while(hasMoreSrcs) { if (NS_FAILED(rv = srcs->HasMoreElements(&hasMoreSrcs))) break; if (hasMoreSrcs == PR_FALSE) break; nsCOMPtr isupports2; if (NS_FAILED(rv = srcs->GetNext(getter_AddRefs(isupports2)))) break; nsCOMPtr src = do_QueryInterface(isupports2); if (!src) continue; if (src == parentResource) { cellPosVal1 = uri; cellPosVal1.Cut(0, sizeof(kRDFNameSpace_Seq_Prefix)-1); // hack: assume that its a number, so pad out a bit nsAutoString zero("000000"); if (cellPosVal1.Length() < zero.Length()) { cellPosVal1.Insert(zero, 0, PRInt32(zero.Length() - cellPosVal1.Length())); } hasMore = PR_FALSE; hasMoreSrcs = PR_FALSE; break; } } } } } if (cellPosVal1.Length() > 0) { nsCOMPtr nodePosLiteral; gRDFService->GetLiteral(cellPosVal1.GetUnicode(), getter_AddRefs(nodePosLiteral)); *theNode = nodePosLiteral; NS_IF_ADDREF(*theNode); isCollationKey = PR_FALSE; } } return(rv); } nsresult XULSortServiceImpl::GetNodeValue(contentSortInfo *info1, sortPtr sortInfo, PRBool first, PRBool onlyCollationHint, nsIRDFNode **theNode, PRBool &isCollationKey) { nsresult rv; nsCOMPtr res1; nsIContent *node1 = info1->content; *theNode = nsnull; isCollationKey = PR_FALSE; nsCOMPtr dom1 = do_QueryInterface(node1); if(dom1) { res1 = info1->resource; } else { nsCOMPtr htmlDom = do_QueryInterface(node1); if (htmlDom) { nsAutoString htmlID; if (NS_SUCCEEDED(rv = node1->GetAttribute(kNameSpaceID_None, kIdAtom, htmlID)) && (rv == NS_CONTENT_ATTR_HAS_VALUE)) { if (NS_FAILED(rv = gRDFService->GetUnicodeResource(htmlID.GetUnicode(), getter_AddRefs(res1)))) { res1 = nsnull; } info1->resource = res1; } } else { return(NS_ERROR_FAILURE); } } if ((sortInfo->naturalOrderSort == PR_FALSE) && (sortInfo->sortProperty)) { if (res1) { rv = GetResourceValue(info1, sortInfo, first, PR_TRUE, onlyCollationHint, theNode, isCollationKey); if ((rv == NS_RDF_NO_VALUE) || (!*theNode)) { rv = GetResourceValue(info1, sortInfo, first, PR_FALSE, onlyCollationHint, theNode, isCollationKey); } } else { rv = NS_RDF_NO_VALUE; } if ((onlyCollationHint == PR_FALSE) && (NS_FAILED(rv) || (rv == NS_RDF_NO_VALUE))) { nsCOMPtr cell1; if (sortInfo->colIndex >= 0) { rv = GetTreeCell(sortInfo, node1, sortInfo->colIndex, getter_AddRefs(cell1)); } else { cell1 = node1; } if (cell1) { nsAutoString cellVal1; if (NS_SUCCEEDED(rv = GetNodeTextValue(sortInfo, cell1, cellVal1)) && (rv != NS_RDF_NO_VALUE)) { nsCOMPtr nodeLiteral; rv = gRDFService->GetLiteral(cellVal1.GetUnicode(), getter_AddRefs(nodeLiteral)); if (NS_SUCCEEDED(rv)) { *theNode = nodeLiteral; NS_IF_ADDREF(*theNode); isCollationKey = PR_FALSE; } } } } } else if ((sortInfo->naturalOrderSort == PR_TRUE) && (sortInfo->parentContainer)) { nsAutoString cellPosVal1; // check to see if this is a RDF_Seq // Note: this code doesn't handle the aggregated Seq case especially well if ((res1) && (sortInfo->db)) { nsCOMPtr parentResource; nsCOMPtr parentDOMNode = do_QueryInterface(sortInfo->parentContainer); if (parentDOMNode) { if (NS_FAILED(rv = parentDOMNode->GetResource(getter_AddRefs(parentResource)))) { parentResource = nsnull; } } nsCOMPtr arcs; if ((parentResource) && (NS_SUCCEEDED(rv = sortInfo->db->ArcLabelsIn(res1, getter_AddRefs(arcs))))) { PRBool hasMore = PR_TRUE; while(hasMore) { if (NS_FAILED(rv = arcs->HasMoreElements(&hasMore))) break; if (hasMore == PR_FALSE) break; nsCOMPtr isupports; if (NS_FAILED(rv = arcs->GetNext(getter_AddRefs(isupports)))) break; nsCOMPtr property = do_QueryInterface(isupports); if (!property) continue; // hack: it it looks like a RDF_Seq and smells like a RDF_Seq... static const char kRDFNameSpace_Seq_Prefix[] = "http://www.w3.org/1999/02/22-rdf-syntax-ns#_"; const char *uri = nsnull; if (NS_FAILED(rv = property->GetValueConst(&uri))) continue; if (!uri) continue; if (nsCRT::strncasecmp(uri, kRDFNameSpace_Seq_Prefix, sizeof(kRDFNameSpace_Seq_Prefix)-1)) continue; nsCOMPtr srcs; if (NS_FAILED(rv = sortInfo->db->GetSources(property, res1, PR_TRUE, getter_AddRefs(srcs)))) continue; PRBool hasMoreSrcs = PR_TRUE; while(hasMoreSrcs) { if (NS_FAILED(rv = srcs->HasMoreElements(&hasMoreSrcs))) break; if (hasMoreSrcs == PR_FALSE) break; nsCOMPtr isupports2; if (NS_FAILED(rv = srcs->GetNext(getter_AddRefs(isupports2)))) break; nsCOMPtr src = do_QueryInterface(isupports2); if (!src) continue; if (src == parentResource) { cellPosVal1 = uri; cellPosVal1.Cut(0, sizeof(kRDFNameSpace_Seq_Prefix)-1); // hack: assume that its a number, so pad out a bit nsAutoString zero("000000"); if (cellPosVal1.Length() < zero.Length()) { cellPosVal1.Insert(zero, 0, zero.Length() - cellPosVal1.Length()); } hasMore = PR_FALSE; hasMoreSrcs = PR_FALSE; break; } } } } } if (cellPosVal1.Length() > 0) { nsCOMPtr nodePosLiteral; gRDFService->GetLiteral(cellPosVal1.GetUnicode(), getter_AddRefs(nodePosLiteral)); *theNode = nodePosLiteral; NS_IF_ADDREF(*theNode); isCollationKey = PR_FALSE; } } return(rv); } nsresult XULSortServiceImpl::InplaceSort(nsIContent *node1, nsIContent *node2, sortPtr sortInfo, PRInt32 & sortOrder) { PRBool isCollationKey1 = PR_FALSE, isCollationKey2 = PR_FALSE; nsresult rv; sortOrder = 0; nsCOMPtr cellNode1, cellNode2; // rjc: in some cases, the first node is static while the second node changes // per comparison; in these circumstances, we can cache the first node if ((sortInfo->cacheFirstHint == PR_TRUE) && (sortInfo->cacheFirstNode)) { cellNode1 = sortInfo->cacheFirstNode; isCollationKey1 = sortInfo->cacheIsFirstNodeCollationKey; } else { GetNodeValue(node1, sortInfo, PR_TRUE, PR_FALSE, getter_AddRefs(cellNode1), isCollationKey1); if (sortInfo->cacheFirstHint == PR_TRUE) { sortInfo->cacheFirstNode = cellNode1; sortInfo->cacheIsFirstNodeCollationKey = isCollationKey1; } } GetNodeValue(node2, sortInfo, PR_TRUE, isCollationKey1, getter_AddRefs(cellNode2), isCollationKey2); PRBool bothValid = PR_FALSE; rv = CompareNodes(cellNode1, isCollationKey1, cellNode2, isCollationKey2, bothValid, sortOrder); if (sortOrder == 0) { // nodes appear to be equivalent, check for secondary sort criteria if (sortInfo->sortProperty2 != nsnull) { cellNode1 = nsnull; cellNode2 = nsnull; isCollationKey1 = PR_FALSE; isCollationKey2 = PR_FALSE; GetNodeValue(node1, sortInfo, PR_FALSE, PR_FALSE, getter_AddRefs(cellNode1), isCollationKey1); GetNodeValue(node2, sortInfo, PR_FALSE, isCollationKey1, getter_AddRefs(cellNode2), isCollationKey2); bothValid = PR_FALSE; rv = CompareNodes(cellNode1, isCollationKey1, cellNode2, isCollationKey2, bothValid, sortOrder); } } if ((bothValid == PR_TRUE) && (sortInfo->descendingSort == PR_TRUE)) { // descending sort is being imposed, so reverse the sort order sortOrder = -sortOrder; } return(NS_OK); } nsresult XULSortServiceImpl::InplaceSort(contentSortInfo *info1, contentSortInfo *info2, sortPtr sortInfo, PRInt32 & sortOrder) { PRBool isCollationKey1 = PR_FALSE, isCollationKey2 = PR_FALSE; nsresult rv; sortOrder = 0; nsCOMPtr cellNode1, cellNode2; // rjc: in some cases, the first node is static while the second node changes // per comparison; in these circumstances, we can cache the first node if ((sortInfo->cacheFirstHint == PR_TRUE) && (sortInfo->cacheFirstNode)) { cellNode1 = sortInfo->cacheFirstNode; isCollationKey1 = sortInfo->cacheIsFirstNodeCollationKey; } else { GetNodeValue(info1, sortInfo, PR_TRUE, PR_FALSE, getter_AddRefs(cellNode1), isCollationKey1); if (sortInfo->cacheFirstHint == PR_TRUE) { sortInfo->cacheFirstNode = cellNode1; sortInfo->cacheIsFirstNodeCollationKey = isCollationKey1; } } GetNodeValue(info2, sortInfo, PR_TRUE, isCollationKey1, getter_AddRefs(cellNode2), isCollationKey2); PRBool bothValid = PR_FALSE; rv = CompareNodes(cellNode1, isCollationKey1, cellNode2, isCollationKey2, bothValid, sortOrder); if (sortOrder == 0) { // nodes appear to be equivalent, check for secondary sort criteria if (sortInfo->sortProperty2 != nsnull) { cellNode1 = nsnull; cellNode2 = nsnull; isCollationKey1 = PR_FALSE; isCollationKey2 = PR_FALSE; GetNodeValue(info1, sortInfo, PR_FALSE, PR_FALSE, getter_AddRefs(cellNode1), isCollationKey1); GetNodeValue(info2, sortInfo, PR_FALSE, isCollationKey1, getter_AddRefs(cellNode2), isCollationKey2); bothValid = PR_FALSE; rv = CompareNodes(cellNode1, isCollationKey1, cellNode2, isCollationKey2, bothValid, sortOrder); } } if ((bothValid == PR_TRUE) && (sortInfo->descendingSort == PR_TRUE)) { // descending sort is being imposed, so reverse the sort order sortOrder = -sortOrder; } return(NS_OK); } int inplaceSortCallback(const void *data1, const void *data2, void *privateData) { /// Note: inplaceSortCallback is a small C callback stub for NS_QuickSort _sortStruct *sortInfo = (_sortStruct *)privateData; nsIContent *node1 = *(nsIContent **)data1; nsIContent *node2 = *(nsIContent **)data2; PRInt32 sortOrder = 0; nsresult rv; if (nsnull != sortInfo) { rv = XULSortServiceImpl::InplaceSort(node1, node2, sortInfo, sortOrder); } return(sortOrder); } int testSortCallback(const void *data1, const void *data2, void *privateData) { /// Note: inplaceSortCallback is a small C callback stub for NS_QuickSort _sortStruct *sortInfo = (_sortStruct *)privateData; contentSortInfo *info1 = *(contentSortInfo **)data1; contentSortInfo *info2 = *(contentSortInfo **)data2; PRInt32 sortOrder = 0; nsresult rv; if (nsnull != sortInfo) { rv = XULSortServiceImpl::InplaceSort(info1, info2, sortInfo, sortOrder); } return(sortOrder); } static contentSortInfo * CreateContentSortInfo(nsIContent *content, nsIRDFResource * resource) { contentSortInfo * info = new contentSortInfo; if(!info) return nsnull; info->content = content; NS_IF_ADDREF(info->content); info->resource = resource; info->checkedCollation1 = PR_FALSE; info->checkedCollation2 = PR_FALSE; info->checkedSort1 = PR_FALSE; info->checkedSort2 = PR_FALSE; info->checkedNode1 = PR_FALSE; info->checkedNode2 = PR_FALSE; return info; } #ifdef TEST_SORT nsresult XULSortServiceImpl::SortTreeChildren(nsIContent *container, sortPtr sortInfo) { PRInt32 childIndex = 0, loop, numChildren = 0, numElements = 0, currentElement, nameSpaceID; nsCOMPtr child; nsresult rv; if (NS_FAILED(rv = container->ChildCount(numChildren))) return(rv); if (numChildren < 1) return(NS_OK); nsCOMPtr doc; container->GetDocument(*getter_AddRefs(doc)); // Note: This is a straight allocation (not a COMPtr) so we // can't return out of this routine until/unless we free it! contentSortInfo ** contentSortInfoArray = new contentSortInfo*[numChildren + 1]; if(!contentSortInfoArray) return NS_ERROR_OUT_OF_MEMORY; // Note: walk backwards (and add nodes into the array backwards) because // we also remove the nodes in this loop [via RemoveChildAt()] and if we // were to do this in a forward-looking manner it would be harder // (since we also skip over non XUL:treeitem nodes) nsCOMPtr tag; currentElement = numChildren; for (childIndex=numChildren-1; childIndex >= 0; childIndex--) { if (NS_FAILED(rv = container->ChildAt(childIndex, *getter_AddRefs(child)))) continue; if (NS_FAILED(rv = child->GetNameSpaceID(nameSpaceID))) continue; if (nameSpaceID == kNameSpaceID_XUL) { if (NS_FAILED(rv = child->GetTag(*getter_AddRefs(tag)))) continue; if (tag.get() == kTreeItemAtom) { --currentElement; nsIRDFResource *resource; gXULUtils->GetElementResource(child, &resource); contentSortInfo *contentInfo = CreateContentSortInfo(child, resource); if(contentInfo) contentSortInfoArray[currentElement] = contentInfo; ++numElements; // immediately remove the child node, and ignore any errors container->RemoveChildAt(childIndex, PR_FALSE); } } } if (numElements > 0) { /* smart sorting (sort within separators) on name column */ if (sortInfo->inbetweenSeparatorSort == PR_TRUE) { PRInt32 startIndex=currentElement; nsAutoString type; for (loop=currentElement; loop< currentElement + numElements; loop++) { if (NS_SUCCEEDED(rv = contentSortInfoArray[loop]->content->GetAttribute(kNameSpaceID_None, kRDF_type, type)) && (rv == NS_CONTENT_ATTR_HAS_VALUE)) { if (type.Equals(kURINC_BookmarkSeparator)) { if (loop > startIndex+1) { NS_QuickSort((void *)&contentSortInfoArray[startIndex], loop-startIndex, sizeof(contentSortInfo *), testSortCallback, (void *)sortInfo); startIndex = loop+1; } } } } if (loop > startIndex+1) { NS_QuickSort((void *)&contentSortInfoArray[startIndex], loop-startIndex, sizeof(contentSortInfo *), testSortCallback, (void *)sortInfo); startIndex = loop+1; } } else { NS_QuickSort((void *)(&contentSortInfoArray[currentElement]), numElements, sizeof(contentSortInfo *), testSortCallback, (void *)sortInfo); } nsCOMPtr parentNode; nsAutoString value; PRInt32 childPos = 0; // recurse on grandchildren for (loop=currentElement; loop < currentElement + numElements; loop++) { nsIContent* kid = NS_STATIC_CAST(nsIContent*, contentSortInfoArray[loop]->content); // Because InsertChildAt() only does a // "shallow" SetDocument(), we need to do a // "deep" one now... kid->SetDocument(doc, PR_TRUE); container->InsertChildAt(kid, childPos++, PR_FALSE); parentNode = (nsIContent *)contentSortInfoArray[loop]->content; contentSortInfo * contentSortInfo = contentSortInfoArray[loop]; NS_RELEASE(contentSortInfo->content); delete contentSortInfo; // if its a container, find its treechildren node, and sort those if (NS_FAILED(rv = parentNode->GetAttribute(kNameSpaceID_None, kContainerAtom, value)) || (rv != NS_CONTENT_ATTR_HAS_VALUE) || (!value.EqualsIgnoreCase(trueStr))) continue; if (NS_FAILED(rv = parentNode->ChildCount(numChildren))) continue; for (childIndex=0; childIndexChildAt(childIndex, *getter_AddRefs(child)))) continue; if (NS_FAILED(rv = child->GetNameSpaceID(nameSpaceID))) continue; if (nameSpaceID != kNameSpaceID_XUL) continue; if (NS_FAILED(rv = child->GetTag(*getter_AddRefs(tag)))) continue; if (tag.get() != kTreeChildrenAtom) continue; sortInfo->parentContainer = parentNode; SortTreeChildren(child, sortInfo); } } } delete [] contentSortInfoArray; contentSortInfoArray = nsnull; return(NS_OK); } #else nsresult XULSortServiceImpl::SortTreeChildren(nsIContent *container, sortPtr sortInfo) { PRInt32 childIndex = 0, loop, numChildren = 0, numElements = 0, currentElement, nameSpaceID; nsCOMPtr child; nsresult rv; if (NS_FAILED(rv = container->ChildCount(numChildren))) return(rv); if (numChildren < 1) return(NS_OK); nsCOMPtr doc; container->GetDocument(*getter_AddRefs(doc)); // Note: This is a straight allocation (not a COMPtr) so we // can't return out of this routine until/unless we free it! nsIContent ** flatArray = new nsIContent*[numChildren + 1]; if (!flatArray) return(NS_ERROR_OUT_OF_MEMORY); // Note: walk backwards (and add nodes into the array backwards) because // we also remove the nodes in this loop [via RemoveChildAt()] and if we // were to do this in a forward-looking manner it would be harder // (since we also skip over non XUL:treeitem nodes) nsCOMPtr tag; currentElement = numChildren; for (childIndex=numChildren-1; childIndex >= 0; childIndex--) { if (NS_FAILED(rv = container->ChildAt(childIndex, *getter_AddRefs(child)))) continue; if (NS_FAILED(rv = child->GetNameSpaceID(nameSpaceID))) continue; if (nameSpaceID == kNameSpaceID_XUL) { if (NS_FAILED(rv = child->GetTag(*getter_AddRefs(tag)))) continue; if (tag.get() == kTreeItemAtom) { --currentElement; flatArray[currentElement] = child; NS_ADDREF(flatArray[currentElement]); // Note: addref it here // Bug 6665. This is a hack to "addref" the resources // before we remove them from the content model. This // keeps them from getting released, which causes // performance problems for some datasources. nsIRDFResource *resource; gXULUtils->GetElementResource(flatArray[currentElement], &resource); // Note: we don't release; see part deux below... ++numElements; // immediately remove the child node, and ignore any errors container->RemoveChildAt(childIndex, PR_FALSE); } } } if (numElements > 0) { /* smart sorting (sort within separators) on name column */ if (sortInfo->inbetweenSeparatorSort == PR_TRUE) { PRInt32 startIndex=currentElement; nsAutoString type; for (loop=currentElement; loop< currentElement + numElements; loop++) { if (NS_SUCCEEDED(rv = flatArray[loop]->GetAttribute(kNameSpaceID_None, kRDF_type, type)) && (rv == NS_CONTENT_ATTR_HAS_VALUE)) { if (type.Equals(kURINC_BookmarkSeparator)) { if (loop > startIndex+1) { NS_QuickSort((void *)&flatArray[startIndex], loop-startIndex, sizeof(nsIContent *), inplaceSortCallback, (void *)sortInfo); startIndex = loop+1; } } } } if (loop > startIndex+1) { NS_QuickSort((void *)&flatArray[startIndex], loop-startIndex, sizeof(nsIContent *), inplaceSortCallback, (void *)sortInfo); startIndex = loop+1; } } else { NS_QuickSort((void *)(&flatArray[currentElement]), numElements, sizeof(nsIContent *), inplaceSortCallback, (void *)sortInfo); } nsCOMPtr parentNode; nsAutoString value; PRInt32 childPos = 0; // recurse on grandchildren for (loop=currentElement; loop < currentElement + numElements; loop++) { nsIContent* kid = NS_STATIC_CAST(nsIContent*, flatArray[loop]); // Because InsertChildAt() only does a // "shallow" SetDocument(), we need to do a // "deep" one now... kid->SetDocument(doc, PR_TRUE); container->InsertChildAt(kid, childPos++, PR_FALSE); // Bug 6665, part deux. The Big Hack. nsIRDFResource *resource; gXULUtils->GetElementResource(flatArray[loop], &resource); nsrefcnt refcnt; NS_RELEASE2(resource, refcnt); NS_RELEASE(resource); parentNode = (nsIContent *)flatArray[loop]; NS_RELEASE(flatArray[loop]); // Note: release it here // if its a container, find its treechildren node, and sort those if (NS_FAILED(rv = parentNode->GetAttribute(kNameSpaceID_None, kContainerAtom, value)) || (rv != NS_CONTENT_ATTR_HAS_VALUE) || (!value.EqualsIgnoreCase(trueStr))) continue; if (NS_FAILED(rv = parentNode->ChildCount(numChildren))) continue; for (childIndex=0; childIndexChildAt(childIndex, *getter_AddRefs(child)))) continue; if (NS_FAILED(rv = child->GetNameSpaceID(nameSpaceID))) continue; if (nameSpaceID != kNameSpaceID_XUL) continue; if (NS_FAILED(rv = child->GetTag(*getter_AddRefs(tag)))) continue; if (tag.get() != kTreeChildrenAtom) continue; sortInfo->parentContainer = parentNode; SortTreeChildren(child, sortInfo); } } } delete [] flatArray; flatArray = nsnull; return(NS_OK); } #endif NS_IMETHODIMP XULSortServiceImpl::InsertContainerNode(nsIRDFCompositeDataSource *db, nsRDFSortState *sortState, nsIContent *root, nsIContent *trueParent, nsIContent *container, nsIContent *node, PRBool aNotify) { nsresult rv; nsAutoString sortResource, sortDirection, sortResource2; _sortStruct sortInfo; // get composite db for tree sortInfo.rdfService = gRDFService; sortInfo.db = db; sortInfo.resCache = nsnull; sortInfo.colIndex = -1; sortInfo.parentContainer = trueParent; sortInfo.kTreeCellAtom = kTreeCellAtom; sortInfo.kNameSpaceID_XUL = kNameSpaceID_XUL; sortInfo.sortProperty = nsnull; sortInfo.sortProperty2 = nsnull; sortInfo.inbetweenSeparatorSort = PR_FALSE; sortInfo.cacheFirstHint = PR_TRUE; sortInfo.cacheFirstNode = nsnull; sortInfo.cacheIsFirstNodeCollationKey = PR_FALSE; if (sortState->mCache) { sortInfo.mInner = sortState->mCache; // Note: this can/might be null } else { sortInfo.mInner = nsnull; } if (container != sortState->lastContainer.get()) { sortState->lastContainer = container; sortState->lastWasFirst = PR_FALSE; sortState->lastWasLast = PR_FALSE; } PRBool sortInfoAvailable = PR_FALSE; if (IsTreeElement(root) == PR_TRUE) { // tree, so look for treecol/treecolgroup node(s) which provide sorting info if (NS_SUCCEEDED(rv = GetSortColumnInfo(root, sortResource, sortDirection, sortResource2, sortInfo.inbetweenSeparatorSort))) { sortInfoAvailable = PR_TRUE; } else { sortDirection = naturalStr; } } else { // not a tree, so look for sorting info on root node if (NS_SUCCEEDED(rv = root->GetAttribute(kNameSpaceID_None, kSortResourceAtom, sortResource)) && (rv == NS_CONTENT_ATTR_HAS_VALUE)) { if (NS_SUCCEEDED(rv = root->GetAttribute(kNameSpaceID_None, kSortDirectionAtom, sortDirection)) && (rv == NS_CONTENT_ATTR_HAS_VALUE)) { sortInfoAvailable = PR_TRUE; if (NS_FAILED(rv = root->GetAttribute(kNameSpaceID_None, kSortResource2Atom, sortResource2)) || (rv != NS_CONTENT_ATTR_HAS_VALUE)) { sortResource2.Truncate(); } } } } if (sortInfoAvailable) { if (sortState->sortResource.Equals(sortResource) && sortState->sortResource2.Equals(sortResource2)) { sortInfo.sortProperty = sortState->sortProperty; sortInfo.sortProperty2 = sortState->sortProperty2; sortInfo.sortPropertyColl = sortState->sortPropertyColl; sortInfo.sortPropertyColl2 = sortState->sortPropertyColl2; sortInfo.sortPropertySort = sortState->sortPropertySort; sortInfo.sortPropertySort2 = sortState->sortPropertySort2; } else { nsAutoString temp; // either first time, or must have changing sorting info, so flush state cache sortState->sortProperty = nsnull; sortState->sortProperty2 = nsnull; sortState->sortPropertyColl = nsnull; sortState->sortPropertyColl2 = nsnull; sortState->sortPropertySort = nsnull; sortState->sortPropertySort2 = nsnull; rv = gRDFService->GetUnicodeResource(sortResource.GetUnicode(), getter_AddRefs(sortInfo.sortProperty)); if (NS_FAILED(rv)) return(rv); sortState->sortResource = sortResource; sortState->sortProperty = sortInfo.sortProperty; temp = sortResource; temp += "?collation=true"; rv = gRDFService->GetUnicodeResource(temp.GetUnicode(), getter_AddRefs(sortInfo.sortPropertyColl)); if (NS_FAILED(rv)) return(rv); sortState->sortPropertyColl = sortInfo.sortPropertyColl; temp = sortResource; temp += "?sort=true"; rv = gRDFService->GetUnicodeResource(temp.GetUnicode(), getter_AddRefs(sortInfo.sortPropertySort)); if (NS_FAILED(rv)) return(rv); sortState->sortPropertySort = sortInfo.sortPropertySort; if (sortResource2.Length() > 0) { rv = gRDFService->GetUnicodeResource(sortResource2.GetUnicode(), getter_AddRefs(sortInfo.sortProperty2)); if (NS_FAILED(rv)) return(rv); sortState->sortResource2 = sortResource2; sortState->sortProperty2 = sortInfo.sortProperty2; temp = sortResource2; temp += "?collation=true"; rv = gRDFService->GetUnicodeResource(temp.GetUnicode(), getter_AddRefs(sortInfo.sortPropertyColl2)); if (NS_FAILED(rv)) return(rv); sortState->sortPropertyColl2 = sortInfo.sortPropertyColl2; temp = sortResource2; temp += "?sort=true"; rv = gRDFService->GetUnicodeResource(temp.GetUnicode(), getter_AddRefs(sortInfo.sortPropertySort2)); if (NS_FAILED(rv)) return(rv); sortState->sortPropertySort2 = sortInfo.sortPropertySort2; } } } else { // either first time, or must have changing sorting info, so flush state cache sortState->sortResource.Truncate(); sortState->sortResource2.Truncate(); sortState->sortProperty = nsnull; sortState->sortProperty2 = nsnull; sortState->sortPropertyColl = nsnull; sortState->sortPropertyColl2 = nsnull; sortState->sortPropertySort = nsnull; sortState->sortPropertySort2 = nsnull; } // set up sort order info sortInfo.naturalOrderSort = PR_FALSE; sortInfo.descendingSort = PR_FALSE; if (sortDirection.Equals(descendingStr)) { sortInfo.descendingSort = PR_TRUE; } else if (!sortDirection.Equals(ascendingStr)) { sortInfo.naturalOrderSort = PR_TRUE; } PRBool isContainerRDFSeq = PR_FALSE; if ((sortInfo.db) && (sortInfo.naturalOrderSort == PR_TRUE)) { // walk up the content model to find the REAL // parent container to determine if its a RDF_Seq nsCOMPtr parent = do_QueryInterface(container, &rv); nsCOMPtr aContent; nsCOMPtr doc; if (NS_SUCCEEDED(rv) && parent) { rv = parent->GetDocument(*getter_AddRefs(doc)); if (! doc) { parent = nsnull; } } if (parent) { nsAutoString id; nsCOMPtr tag; if (NS_FAILED(rv = trueParent->GetTag(*getter_AddRefs(tag)))) return(rv); if (tag.get() == kTreeAtom) { // XXX hmmm... currently, if we just check for ref on anything before // checking for id, this gives the sidebar huge fits. So, for the // moment, until the solution is determined, let's restrict checking // for ref before id to only the root node of trees rv = trueParent->GetAttribute(kNameSpaceID_None, kRefAtom, id); } if (id.Length() == 0) { rv = trueParent->GetAttribute(kNameSpaceID_None, kIdAtom, id); } if (id.Length() > 0) { nsCOMPtr containerRes; rv = gXULUtils->MakeElementResource(doc, id, getter_AddRefs(containerRes)); if (NS_SUCCEEDED(rv)) { rv = sortInfo.db->HasAssertion(containerRes, kRDF_instanceOf, kRDF_Seq, PR_TRUE, &isContainerRDFSeq); } } } } PRBool childAdded = PR_FALSE; if ((sortInfo.naturalOrderSort == PR_FALSE) || ((sortInfo.naturalOrderSort == PR_TRUE) && (isContainerRDFSeq == PR_TRUE))) { // figure out where to insert the node when a sort order is being imposed PRInt32 numChildren = 0; if (NS_FAILED(rv = container->ChildCount(numChildren))) return(rv); if (numChildren > 0) { nsCOMPtr child; nsIContent *temp; PRInt32 direction; // rjc says: The following is an implementation of a fairly optimal // binary search insertion sort... with interpolation at either end-point. if (sortState->lastWasFirst == PR_TRUE) { container->ChildAt(0, *getter_AddRefs(child)); temp = child.get(); direction = inplaceSortCallback(&node, &temp, &sortInfo); if (direction < 0) { container->InsertChildAt(node, 0, aNotify); childAdded = PR_TRUE; } else { sortState->lastWasFirst = PR_FALSE; } } else if (sortState->lastWasLast == PR_TRUE) { container->ChildAt(numChildren-1, *getter_AddRefs(child)); temp = child.get(); direction = inplaceSortCallback(&node, &temp, &sortInfo); if (direction > 0) { container->AppendChildTo(node, aNotify); childAdded = PR_TRUE; } else { sortState->lastWasLast = PR_FALSE; } } PRInt32 left = 1, right = numChildren, x; while ((childAdded == PR_FALSE) && (right >= 1)) { x = (left + right) / 2; container->ChildAt(x-1, *getter_AddRefs(child)); temp = child.get(); // rjc says: since cacheFirstHint is PR_TRUE, the first node passed // into inplaceSortCallback() must be the node that doesn't change direction = inplaceSortCallback(&node, &temp, &sortInfo); if (((x == left) && (direction < 0)) || (((x == right)) && (direction > 0)) || (left == right)) { PRInt32 thePos = ((direction > 0) ? x : x-1); container->InsertChildAt(node, thePos, aNotify); childAdded = PR_TRUE; sortState->lastWasFirst = (thePos == 0) ? PR_TRUE: PR_FALSE; sortState->lastWasLast = (thePos >= numChildren) ? PR_TRUE: PR_FALSE; break; } if (direction < 0) right = x-1; else left = x+1; } } } if (childAdded == PR_FALSE) { container->AppendChildTo(node, aNotify); } if ((!sortState->mCache) && (sortInfo.mInner)) { sortState->mCache = sortInfo.mInner; } return(NS_OK); } NS_IMETHODIMP XULSortServiceImpl::Sort(nsIDOMNode* node, const char *sortResource, const char *sortDirection) { nsAutoString sortRes(sortResource), sortDir(sortDirection); nsresult rv = DoSort(node, sortResource, sortDirection); return(rv); } nsresult XULSortServiceImpl::DoSort(nsIDOMNode* node, const nsString& sortResource, const nsString& sortDirection) { PRInt32 treeBodyIndex; nsresult rv; _sortStruct sortInfo; // get tree node nsCOMPtr contentNode = do_QueryInterface(node); if (!contentNode) return(NS_ERROR_FAILURE); nsCOMPtr treeNode; if (NS_FAILED(rv = FindTreeElement(nsnull, contentNode, getter_AddRefs(treeNode)))) return(rv); nsCOMPtr domXulTree = do_QueryInterface(treeNode); if (!domXulTree) return(NS_ERROR_FAILURE); // get composite db for tree sortInfo.rdfService = gRDFService; sortInfo.db = nsnull; sortInfo.resCache = nsnull; sortInfo.mInner = nsnull; sortInfo.parentContainer = treeNode; sortInfo.inbetweenSeparatorSort = PR_FALSE; sortInfo.cacheFirstHint = PR_FALSE; sortInfo.cacheFirstNode = nsnull; sortInfo.cacheIsFirstNodeCollationKey = PR_FALSE; // remove any sort hints on tree root node treeNode->UnsetAttribute(kNameSpaceID_None, kSortActiveAtom, PR_FALSE); treeNode->UnsetAttribute(kNameSpaceID_None, kSortDirectionAtom, PR_FALSE); treeNode->UnsetAttribute(kNameSpaceID_None, kSortSeparatorsAtom, PR_FALSE); treeNode->UnsetAttribute(kNameSpaceID_RDF, kResourceAtom, PR_FALSE); treeNode->UnsetAttribute(kNameSpaceID_RDF, kResource2Atom, PR_FALSE); nsCOMPtr cds; if (NS_SUCCEEDED(rv = domXulTree->GetDatabase(getter_AddRefs(cds)))) { sortInfo.db = cds; } sortInfo.kTreeCellAtom = kTreeCellAtom; sortInfo.kNameSpaceID_XUL = kNameSpaceID_XUL; // determine new sort resource and direction to use if (sortDirection.Equals(naturalStr)) { sortInfo.naturalOrderSort = PR_TRUE; sortInfo.descendingSort = PR_FALSE; } else { sortInfo.naturalOrderSort = PR_FALSE; if (sortDirection.Equals(ascendingStr)) sortInfo.descendingSort = PR_FALSE; else if (sortDirection.Equals(descendingStr)) sortInfo.descendingSort = PR_TRUE; } // get index of sort column, find tree body, and sort. The sort // _won't_ send any notifications, so we won't trigger any reflows... nsAutoString sortResource2; PRBool found; if (NS_FAILED(rv = GetSortColumnIndex(treeNode, sortResource, sortDirection, sortResource2, sortInfo.inbetweenSeparatorSort, sortInfo.colIndex, found))) return(rv); rv = gRDFService->GetUnicodeResource(sortResource.GetUnicode(), getter_AddRefs(sortInfo.sortProperty)); if (NS_FAILED(rv)) return(rv); nsAutoString temp; temp = sortResource; temp += "?collation=true"; rv = gRDFService->GetUnicodeResource(temp.GetUnicode(), getter_AddRefs(sortInfo.sortPropertyColl)); if (NS_FAILED(rv)) return(rv); temp = sortResource; temp += "?sort=true"; rv = gRDFService->GetUnicodeResource(temp.GetUnicode(), getter_AddRefs(sortInfo.sortPropertySort)); if (NS_FAILED(rv)) return(rv); if (sortResource2.Length() > 0) { rv = gRDFService->GetUnicodeResource(sortResource2.GetUnicode(), getter_AddRefs(sortInfo.sortProperty2)); if (NS_FAILED(rv)) return(rv); temp = sortResource2; temp += "?collation=true"; rv = gRDFService->GetUnicodeResource(temp.GetUnicode(), getter_AddRefs(sortInfo.sortPropertyColl2)); if (NS_FAILED(rv)) return(rv); temp = sortResource2; temp += "?sort=true"; rv = gRDFService->GetUnicodeResource(temp.GetUnicode(), getter_AddRefs(sortInfo.sortPropertySort2)); if (NS_FAILED(rv)) return(rv); } SetSortHints(treeNode, sortResource, sortDirection, sortResource2, sortInfo.inbetweenSeparatorSort, found); nsCOMPtr treeBody; if (NS_FAILED(rv = FindTreeChildrenElement(treeNode, getter_AddRefs(treeBody)))) return(rv); if (NS_SUCCEEDED(rv = SortTreeChildren(treeBody, &sortInfo))) { } // Now remove the treebody and re-insert it to force the frames to be rebuilt. nsCOMPtr treeParent; if (NS_FAILED(rv = treeBody->GetParent(*getter_AddRefs(treeParent)))) return(rv); if (NS_FAILED(rv = treeParent->IndexOf(treeBody, treeBodyIndex))) return(rv); if (NS_FAILED(rv = treeParent->RemoveChildAt(treeBodyIndex, PR_TRUE))) return(rv); // We need to do a deep SetDocument(), because AppendChildTo() // only does a shallow one. nsCOMPtr doc; treeParent->GetDocument(*getter_AddRefs(doc)); treeBody->SetDocument(doc, PR_TRUE); if (NS_FAILED(rv = treeParent->AppendChildTo(treeBody, PR_TRUE))) return(rv); return(NS_OK); } nsresult NS_NewXULSortService(nsIXULSortService** mgr) { XULSortServiceImpl *sortService = new XULSortServiceImpl(); if (!sortService) return(NS_ERROR_OUT_OF_MEMORY); *mgr = sortService; NS_ADDREF(*mgr); return(NS_OK); }