Back to index

lightning-sunbird  0.9+nobinonly
nsAbOSXDirectory.mm
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Peter Van der Beken.
00019  * Portions created by the Initial Developer are Copyright (C) 2004
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Peter Van der Beken <peterv@propagandism.org>
00024  *
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsAbOSXDirectory.h"
00041 #include "nsAbOSXCard.h"
00042 #include "nsAbOSXUtils.h"
00043 #include "nsAbQueryStringToExpression.h"
00044 #include "nsArrayEnumerator.h"
00045 #include "nsAutoPtr.h"
00046 #include "nsCOMArray.h"
00047 #include "nsEnumeratorUtils.h"
00048 #include "nsIAbDirectoryQueryProxy.h"
00049 #include "nsIAddrBookSession.h"
00050 #include "nsIRDFService.h"
00051 #include "nsServiceManagerUtils.h"
00052 
00053 #include <AddressBook/AddressBook.h>
00054 
00055 static nsresult
00056 ConvertToGroupResource(nsIRDFService *aRDFService, NSString *aUid,
00057                        nsIAbDirectory **aResult)
00058 {
00059     NS_ASSERTION(aUid, "No UID for group!.");
00060 
00061     *aResult = nsnull;
00062 
00063     nsCAutoString uri(NS_ABOSXDIRECTORY_URI_PREFIX);
00064     AppendToCString(aUid, uri);
00065 
00066     nsCOMPtr<nsIRDFResource> resource;
00067     nsresult rv = aRDFService->GetResource(uri, getter_AddRefs(resource));
00068     NS_ENSURE_SUCCESS(rv, rv);
00069 
00070     return CallQueryInterface(resource, aResult);
00071 }
00072 
00073 static nsresult
00074 ConvertToCard(nsIRDFService *aRDFService, ABRecord *aRecord,
00075               nsIAbCard **aResult)
00076 {
00077     *aResult = nsnull;
00078 
00079     NSString *uid = [aRecord uniqueId];
00080     NS_ASSERTION(uid, "No UID for card!.");
00081     if (!uid) {
00082         return NS_ERROR_FAILURE;
00083     }
00084 
00085     nsCAutoString uri(NS_ABOSXCARD_URI_PREFIX);
00086     AppendToCString(uid, uri);
00087 
00088     nsCOMPtr<nsIRDFResource> resource;
00089     nsresult rv = aRDFService->GetResource(uri, getter_AddRefs(resource));
00090     NS_ENSURE_SUCCESS(rv, rv);
00091 
00092     return CallQueryInterface(resource, aResult);
00093 }
00094 
00095 static nsresult
00096 Update(nsIRDFService *aRDFService, NSString *aUid)
00097 {
00098     ABAddressBook *addressBook = [ABAddressBook sharedAddressBook];
00099     ABRecord *card = [addressBook recordForUniqueId:aUid];
00100     if ([card isKindOfClass:[ABGroup class]]) {
00101         nsCOMPtr<nsIAbDirectory> directory;
00102         ConvertToGroupResource(aRDFService, aUid, getter_AddRefs(directory));
00103         nsCOMPtr<nsIAbOSXDirectory> osxDirectory =
00104             do_QueryInterface(directory);
00105 
00106         osxDirectory->Update();
00107     }
00108     else {
00109         nsCOMPtr<nsIAbCard> abCard;
00110         ConvertToCard(aRDFService, card, getter_AddRefs(abCard));
00111         nsCOMPtr<nsIAbOSXCard> osxCard = do_QueryInterface(abCard);
00112 
00113         osxCard->Update(PR_TRUE);
00114     }
00115     return NS_OK;
00116 }
00117 
00118 @interface ABChangedMonitor : NSObject
00119 -(void)ABChanged:(NSNotification *)aNotification;
00120 @end
00121 
00122 @implementation ABChangedMonitor
00123 -(void)ABChanged:(NSNotification *)aNotification
00124 {
00125     NSDictionary *changes = [aNotification userInfo];
00126 
00127     nsresult rv;
00128     nsCOMPtr<nsIRDFService> rdfService =
00129         do_GetService("@mozilla.org/rdf/rdf-service;1", &rv);
00130     NS_ENSURE_SUCCESS(rv, );
00131 
00132     NSArray *inserted = [changes objectForKey:kABInsertedRecords];
00133     if (inserted) {
00134         nsCOMPtr<nsIRDFResource> resource;
00135         rv = rdfService->GetResource(NS_LITERAL_CSTRING("moz-abOSXdirectory:///"),
00136                                      getter_AddRefs(resource));
00137         NS_ENSURE_SUCCESS(rv, );
00138 
00139         nsCOMPtr<nsIAbOSXDirectory> osxDirectory =
00140             do_QueryInterface(resource, &rv);
00141         NS_ENSURE_SUCCESS(rv, );
00142 
00143         nsCOMPtr<nsIAddrBookSession> abSession =
00144             do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &rv);
00145         NS_ENSURE_SUCCESS(rv, );
00146 
00147         unsigned int i, count = [inserted count];
00148         for (i = 0; i < count; ++i) {
00149             ABAddressBook *addressBook =
00150                  [ABAddressBook sharedAddressBook];
00151             ABRecord *card =
00152                 [addressBook recordForUniqueId:[inserted objectAtIndex:i]];
00153             if ([card isKindOfClass:[ABGroup class]]) {
00154                 nsCOMPtr<nsIAbDirectory> directory;
00155                 ConvertToGroupResource(rdfService, [inserted objectAtIndex:i],
00156                                        getter_AddRefs(directory));
00157 
00158                 rv = osxDirectory->AssertDirectory(abSession, directory);
00159                 NS_ENSURE_SUCCESS(rv, );
00160             }
00161             else {
00162                 nsCOMPtr<nsIAbCard> abCard;
00163                 ConvertToCard(rdfService, card, getter_AddRefs(abCard));
00164 
00165                 rv = osxDirectory->AssertCard(abSession, abCard);
00166                 NS_ENSURE_SUCCESS(rv, );
00167             }
00168         }
00169     }
00170 
00171     NSArray *updated = [changes objectForKey:kABUpdatedRecords];
00172     if (updated) {
00173         unsigned int i, count = [updated count];
00174         for (i = 0; i < count; ++i) {
00175             NSString *uid = [updated objectAtIndex:i];
00176             Update(rdfService, uid);
00177         }
00178     }
00179 
00180     NSArray *deleted = [changes objectForKey:kABDeletedRecords];
00181     if (deleted) {
00182         nsCOMPtr<nsIRDFResource> resource;
00183         rv = rdfService->GetResource(NS_LITERAL_CSTRING("moz-abOSXdirectory:///"),
00184                                      getter_AddRefs(resource));
00185         NS_ENSURE_SUCCESS(rv, );
00186 
00187         nsCOMPtr<nsIAbOSXDirectory> osxDirectory =
00188             do_QueryInterface(resource, &rv);
00189         NS_ENSURE_SUCCESS(rv, );
00190 
00191         nsCOMPtr<nsIAddrBookSession> abSession =
00192             do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &rv);
00193         NS_ENSURE_SUCCESS(rv, );
00194 
00195         unsigned int i, count = [deleted count];
00196         for (i = 0; i < count; ++i) {
00197             ABAddressBook *addressBook = [ABAddressBook sharedAddressBook];
00198             NSString *recordClass =
00199                 [addressBook recordClassFromUniqueId:[deleted objectAtIndex:i]];
00200             if ([recordClass isEqualToString:@"ABGroup"]) {
00201                 nsCOMPtr<nsIAbDirectory> directory;
00202                 ConvertToGroupResource(rdfService, [deleted objectAtIndex:i],
00203                                        getter_AddRefs(directory));
00204 
00205                 rv = osxDirectory->UnassertDirectory(abSession, directory);
00206                 NS_ENSURE_SUCCESS(rv, );
00207             }
00208             else {
00209                 nsCOMPtr<nsIAbCard> abCard;
00210                 ConvertToCard(rdfService, [deleted objectAtIndex:i],
00211                               getter_AddRefs(abCard));
00212 
00213                 rv = osxDirectory->UnassertCard(abSession, abCard);
00214                 NS_ENSURE_SUCCESS(rv, );
00215             }
00216         }
00217     }
00218 
00219     if (!inserted && !updated && !deleted) {
00220         // XXX This is supposed to mean "everything was updated", but we get
00221         //     this whenever something has changed, so not sure what to do.
00222     }
00223 
00224     [[aNotification object] release];
00225 }
00226 @end
00227 
00228 static nsresult
00229 MapConditionString(nsIAbBooleanConditionString *aCondition, PRBool aNegate,
00230                    PRBool &aCanHandle, ABSearchElement **aResult)
00231 {
00232     aCanHandle = PR_FALSE;
00233 
00234     nsAbBooleanConditionType conditionType = 0;
00235     nsresult rv = aCondition->GetCondition(&conditionType);
00236     NS_ENSURE_SUCCESS(rv, rv);
00237 
00238     ABSearchComparison comparison;
00239     switch (conditionType) {
00240         case nsIAbBooleanConditionTypes::Contains:
00241         {
00242             if (!aNegate) {
00243                 comparison = kABContainsSubString;
00244                 aCanHandle = PR_TRUE;
00245             }
00246             break;
00247         }
00248         case nsIAbBooleanConditionTypes::DoesNotContain:
00249         {
00250             if (aNegate) {
00251                 comparison = kABContainsSubString;
00252                 aCanHandle = PR_TRUE;
00253             }
00254             break;
00255         }
00256         case nsIAbBooleanConditionTypes::Is:
00257         {
00258             comparison = aNegate ? kABNotEqual : kABEqual;
00259             aCanHandle = PR_TRUE;
00260             break;
00261         }
00262         case nsIAbBooleanConditionTypes::IsNot:
00263         {
00264             comparison = aNegate ? kABEqual : kABNotEqual;
00265             aCanHandle = PR_TRUE;
00266             break;
00267         }
00268         case nsIAbBooleanConditionTypes::BeginsWith:
00269         {
00270             if (!aNegate) {
00271                 comparison = kABPrefixMatch;
00272                 aCanHandle = PR_TRUE;
00273             }
00274             break;
00275         }
00276         case nsIAbBooleanConditionTypes::EndsWith:
00277         {
00278             //comparison = kABSuffixMatch;
00279             break;
00280         }
00281         case nsIAbBooleanConditionTypes::LessThan:
00282         {
00283             comparison = aNegate ? kABGreaterThanOrEqual : kABLessThan;
00284             aCanHandle = PR_TRUE;
00285             break;
00286         }
00287         case nsIAbBooleanConditionTypes::GreaterThan:
00288         {
00289             comparison = aNegate ? kABLessThanOrEqual : kABGreaterThan;
00290             aCanHandle = PR_TRUE;
00291             break;
00292         }
00293     }
00294 
00295     if (!aCanHandle) {
00296         return NS_OK;
00297     }
00298 
00299     nsXPIDLCString name;
00300     rv = aCondition->GetName(getter_Copies(name));
00301     NS_ENSURE_SUCCESS(rv, rv);
00302 
00303     nsXPIDLString value;
00304     rv = aCondition->GetValue(getter_Copies(value));
00305     NS_ENSURE_SUCCESS(rv, rv);
00306 
00307     PRUint32 length = value.Length();
00308 
00309     PRUint32 i;
00310     for (i = 0; i < nsAbOSXUtils::kPropertyMapSize; ++i) {
00311         if (name.EqualsASCII(nsAbOSXUtils::kPropertyMap[i].mPropertyName)) {
00312             *aResult =
00313                 [ABPerson searchElementForProperty:nsAbOSXUtils::kPropertyMap[i].mOSXProperty
00314                                              label:nsAbOSXUtils::kPropertyMap[i].mOSXLabel
00315                                                key:nsAbOSXUtils::kPropertyMap[i].mOSXKey
00316                                              value:[NSString stringWithCharacters:value.get() length:length]
00317                                         comparison:comparison];
00318 
00319             return NS_OK;
00320         }
00321     }
00322 
00323     if (name.EqualsLiteral("DisplayName") && comparison == kABContainsSubString) {
00324         ABSearchElement *first =
00325                 [ABPerson searchElementForProperty:kABFirstNameProperty
00326                                              label:nil
00327                                                key:nil
00328                                              value:[NSString stringWithCharacters:value.get() length:length]
00329                                         comparison:comparison];
00330         ABSearchElement *second =
00331                 [ABPerson searchElementForProperty:kABLastNameProperty
00332                                              label:nil
00333                                                key:nil
00334                                              value:[NSString stringWithCharacters:value.get() length:length]
00335                                         comparison:comparison];
00336         ABSearchElement *third =
00337                 [ABGroup searchElementForProperty:kABGroupNameProperty
00338                                              label:nil
00339                                                key:nil
00340                                              value:[NSString stringWithCharacters:value.get() length:length]
00341                                         comparison:comparison];
00342 
00343         *aResult = [ABSearchElement searchElementForConjunction:kABSearchOr children:[NSArray arrayWithObjects:first, second, third, nil]];
00344 
00345         return NS_OK;
00346     }
00347 
00348     aCanHandle = PR_FALSE;
00349 
00350     return NS_OK;
00351 }
00352 
00353 static nsresult
00354 BuildSearchElements(nsIAbBooleanExpression *aExpression,
00355                     PRBool &aCanHandle,
00356                     ABSearchElement **aResult)
00357 {
00358     aCanHandle = PR_TRUE;
00359 
00360     nsCOMPtr<nsISupportsArray> expressions;
00361     nsresult rv = aExpression->GetExpressions(getter_AddRefs(expressions));
00362     NS_ENSURE_SUCCESS(rv, rv);
00363 
00364     nsAbBooleanOperationType operation;
00365     rv = aExpression->GetOperation(&operation);
00366     NS_ENSURE_SUCCESS(rv, rv);
00367 
00368     PRUint32 count;
00369     rv = expressions->Count(&count);
00370     NS_ENSURE_SUCCESS(rv, rv);
00371 
00372     NS_ASSERTION(count > 1 && operation != nsIAbBooleanOperationTypes::NOT,
00373                  "This doesn't make sense!");
00374 
00375     NSMutableArray *array = nsnull;
00376     if (count > 1) {
00377         array = [[NSMutableArray alloc] init];
00378     }
00379 
00380     PRUint32 i;
00381     nsCOMPtr<nsIAbBooleanConditionString> condition;
00382     nsCOMPtr<nsIAbBooleanExpression> subExpression;
00383     for (i = 0; i < count; ++i) {
00384         ABSearchElement *element = nsnull;
00385 
00386         condition = do_QueryElementAt(expressions, i);
00387         if (condition) {
00388             rv = MapConditionString(condition, operation == nsIAbBooleanOperationTypes::NOT, aCanHandle, &element);
00389             NS_ENSURE_SUCCESS(rv, rv);
00390         }
00391         else {
00392             subExpression = do_QueryElementAt(expressions, i);
00393             if (subExpression) {
00394                 rv = BuildSearchElements(subExpression, aCanHandle, &element);
00395                 NS_ENSURE_SUCCESS(rv, rv);
00396             }
00397         }
00398 
00399         if (!aCanHandle) {
00400             return NS_OK;
00401         }
00402 
00403         if (element) {
00404             if (array) {
00405                 [array addObject:element];
00406             }
00407             else {
00408                 *aResult = element;
00409             }
00410         }
00411     }
00412 
00413     if (array) {
00414         ABSearchConjunction conjunction = operation == nsIAbBooleanOperationTypes::AND ? kABSearchAnd : kABSearchOr;
00415         *aResult = [ABSearchElement searchElementForConjunction:conjunction children:array];
00416     }
00417 
00418     return NS_OK;
00419 }
00420 
00421 static PRBool
00422 Search(nsIAbBooleanExpression *aExpression, NSArray **aResult)
00423 {
00424     PRBool canHandle = PR_FALSE;
00425     ABSearchElement *searchElement;
00426     nsresult rv = BuildSearchElements(aExpression, canHandle, &searchElement);
00427     NS_ENSURE_SUCCESS(rv, PR_FALSE);
00428 
00429     if (canHandle) {
00430         *aResult = [[ABAddressBook sharedAddressBook] recordsMatchingSearchElement:searchElement];
00431     }
00432 
00433     return canHandle;
00434 }
00435 
00436 static PRUint32 sObserverCount = 0;
00437 static ABChangedMonitor *sObserver = nsnull;
00438 
00439 nsAbOSXDirectory::~nsAbOSXDirectory()
00440 {
00441     if (--sObserverCount == 0) {
00442         [[NSNotificationCenter defaultCenter] removeObserver:sObserver];
00443         [sObserver release];
00444     }
00445 }
00446 
00447 NS_IMPL_ISUPPORTS_INHERITED2(nsAbOSXDirectory,
00448                              nsAbDirectoryRDFResource,
00449                              nsIAbDirectory,
00450                              nsIAbOSXDirectory)
00451 
00452 NS_IMETHODIMP
00453 nsAbOSXDirectory::Init(const char *aUri)
00454 {
00455     ABAddressBook *addressBook = [ABAddressBook sharedAddressBook];
00456     if (sObserverCount == 0) {
00457         sObserver = [[ABChangedMonitor alloc] init];
00458         [[NSNotificationCenter defaultCenter] addObserver:(ABChangedMonitor*)sObserver
00459            selector:@selector(ABChanged:)
00460            name:kABDatabaseChangedExternallyNotification
00461            object:nil];
00462     }
00463     ++sObserverCount;
00464 
00465     nsresult rv = nsAbDirectoryRDFResource::Init(aUri);
00466     NS_ENSURE_SUCCESS(rv, rv);
00467 
00468     if (mURINoQuery.Length() > 22) {
00469         nsCAutoString uid(Substring(mURINoQuery, 21));
00470         ABRecord *card = [addressBook recordForUniqueId:[NSString stringWithUTF8String:uid.get()]];
00471         NS_ASSERTION([card isKindOfClass:[ABGroup class]], "Huh.");
00472 
00473         m_IsMailList = PR_TRUE;
00474         AppendToString([card valueForProperty:kABGroupNameProperty], m_DirName);
00475     }
00476 
00477     return NS_OK;
00478 }
00479 
00480 NS_IMETHODIMP
00481 nsAbOSXDirectory::GetOperations(PRInt32 *aOperations)
00482 {
00483     *aOperations = nsIAbDirectory::opRead |
00484                    nsIAbDirectory::opSearch;
00485 
00486     return NS_OK;
00487 }
00488 
00489 struct nsEnumeratorData
00490 {
00491     NSMutableArray *mCards;
00492     nsIAbDirectory *mDirectory;
00493     nsIAddrBookSession *mSession;
00494 };
00495 
00496 PLDHashOperator
00497 Enumerator(nsIAbCardHashKey *aKey, void *aUserArg)
00498 {
00499     nsEnumeratorData *data = NS_STATIC_CAST(nsEnumeratorData*, aUserArg);
00500 
00501     nsIAbCard *abCard = aKey->GetCard();
00502 
00503     nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(abCard);
00504 
00505     const char* uri;
00506     resource->GetValueConst(&uri);
00507     NSString *uid = [NSString stringWithUTF8String:(uri + 21)];
00508 
00509     unsigned int i, count = [data->mCards count];
00510     for (i = 0; i < count; ++i) {
00511         if ([[[data->mCards objectAtIndex:i] uniqueId] isEqualToString:uid]) {
00512             [data->mCards removeObjectAtIndex:i];
00513             break;
00514         }
00515     }
00516 
00517     if (i == count) {
00518         data->mSession->NotifyDirectoryItemDeleted(data->mDirectory, abCard);
00519 
00520         return PL_DHASH_REMOVE;
00521     }
00522 
00523     return PL_DHASH_NEXT;
00524 }
00525 
00526 nsresult
00527 nsAbOSXDirectory::Update()
00528 {
00529     nsresult rv;
00530     nsCOMPtr<nsIAddrBookSession> abSession = do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &rv);
00531     NS_ENSURE_SUCCESS(rv, rv);
00532 
00533     if (mIsQueryURI) {
00534         return NS_OK;
00535     }
00536 
00537     ABAddressBook *addressBook = [ABAddressBook sharedAddressBook];
00538 
00539     NSArray *groups, *cards;
00540     if (m_IsMailList) {
00541         ABGroup *group = (ABGroup*)[addressBook recordForUniqueId:[NSString stringWithUTF8String:nsCAutoString(Substring(mURINoQuery, 21)).get()]];
00542         groups = nil;
00543         cards = [[group members] arrayByAddingObjectsFromArray:[group subgroups]];
00544     }
00545     else {
00546         groups = [addressBook groups];
00547         cards = [[addressBook people] arrayByAddingObjectsFromArray:groups];
00548     }
00549 
00550     NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:cards];
00551     if (mCardList.IsInitialized()) {
00552         nsEnumeratorData data = { mutableArray, this, abSession };
00553 
00554         mCardList.EnumerateEntries(Enumerator, &data);
00555     }
00556 
00557     NSEnumerator *enumerator = [mutableArray objectEnumerator];
00558     ABRecord *card;
00559     nsCOMPtr<nsIAbCard> abCard;
00560     while ((card = [enumerator nextObject])) {
00561         rv = ConvertToCard(gRDFService, card, getter_AddRefs(abCard));
00562         NS_ENSURE_SUCCESS(rv, rv);
00563 
00564         AssertCard(abSession, abCard);
00565     }
00566 
00567     card = (ABRecord*)[addressBook recordForUniqueId:[NSString stringWithUTF8String:nsCAutoString(Substring(mURINoQuery, 21)).get()]];
00568     NSString * stringValue = [card valueForProperty:kABGroupNameProperty];
00569     if (![stringValue isEqualToString:WrapString(m_DirName)]) {
00570         nsAutoString oldValue(m_DirName);
00571         AssignToString(stringValue, m_DirName);
00572         nsISupports *supports =
00573             NS_ISUPPORTS_CAST(nsAbDirectoryRDFResource*, this);
00574         abSession->NotifyItemPropertyChanged(supports, "DirName",
00575                                              oldValue.get(), m_DirName.get());
00576     }
00577 
00578     if (groups) {
00579         mutableArray = [NSMutableArray arrayWithArray:groups];
00580         nsCOMPtr<nsIAbDirectory> directory;
00581         if (m_AddressList) {
00582             PRUint32 i, count;
00583             m_AddressList->Count(&count);
00584             for (i = 0; i < count; ++i) {
00585                 directory = do_QueryElementAt(m_AddressList, i);
00586                 nsCOMPtr<nsIAbOSXDirectory> osxDirectory =
00587                     do_QueryInterface(directory);
00588 
00589                 nsCAutoString uri;
00590                 osxDirectory->GetURI(uri);
00591                 uri.Cut(0, 21);
00592                 NSString *uid = [NSString stringWithUTF8String:uri.get()];
00593 
00594                 unsigned int j, arrayCount = [mutableArray count];
00595                 for (j = 0; j < arrayCount; ++j) {
00596                     if ([[[mutableArray objectAtIndex:j] uniqueId] isEqualToString:uid]) {
00597                         [mutableArray removeObjectAtIndex:j];
00598                         break;
00599                     }
00600                 }
00601 
00602                 if (j == arrayCount) {
00603                     UnassertDirectory(abSession, directory);
00604                 }
00605             }
00606         }
00607 
00608         enumerator = [mutableArray objectEnumerator];
00609         while ((card = [enumerator nextObject])) {
00610             rv = ConvertToGroupResource(gRDFService, [card uniqueId],
00611                                         getter_AddRefs(directory));
00612             NS_ENSURE_SUCCESS(rv, rv);
00613 
00614             AssertDirectory(abSession, directory);
00615         }
00616     }
00617 
00618     return NS_OK;
00619 }
00620 
00621 nsresult
00622 nsAbOSXDirectory::AssertChildNodes()
00623 {
00624     // Queries and mailing lists can't have childnodes.
00625     if (mIsQueryURI || m_IsMailList) {
00626         return NS_OK;
00627     }
00628 
00629     nsresult rv;
00630     nsCOMPtr<nsIAddrBookSession> abSession =
00631         do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &rv);
00632     NS_ENSURE_SUCCESS(rv, rv);
00633 
00634     NSArray *groups = [[ABAddressBook sharedAddressBook] groups];
00635 
00636     unsigned int i, count = [groups count];
00637 
00638     if (count > 0 && !m_AddressList) {
00639         rv = NS_NewISupportsArray(getter_AddRefs(m_AddressList));
00640         NS_ENSURE_SUCCESS(rv, rv);
00641     }
00642 
00643     nsCOMPtr<nsIAbDirectory> directory;
00644     for (i = 0; i < count; ++i) {
00645         rv = ConvertToGroupResource(gRDFService, [[groups objectAtIndex:i] uniqueId],
00646                                     getter_AddRefs(directory));
00647         NS_ENSURE_SUCCESS(rv, rv);
00648 
00649         rv = AssertDirectory(abSession, directory);
00650         NS_ENSURE_SUCCESS(rv, rv);
00651     }
00652 
00653     return NS_OK;
00654 }
00655 
00656 nsresult
00657 nsAbOSXDirectory::AssertDirectory(nsIAddrBookSession *aSession,
00658                                   nsIAbDirectory *aDirectory)
00659 {
00660     NS_ASSERTION(!m_AddressList || m_AddressList->IndexOf(aDirectory) < 0,
00661                  "Replacing?");
00662 
00663     nsresult rv;
00664     if (!m_AddressList) {
00665         rv = NS_NewISupportsArray(getter_AddRefs(m_AddressList));
00666         NS_ENSURE_SUCCESS(rv, rv);
00667     }
00668 
00669     rv = m_AddressList->AppendElement(aDirectory);
00670     NS_ENSURE_SUCCESS(rv, rv);
00671 
00672     return aSession->NotifyDirectoryItemAdded(this, aDirectory);
00673 }
00674 
00675 nsresult
00676 nsAbOSXDirectory::AssertCard(nsIAddrBookSession *aSession,
00677                              nsIAbCard *aCard)
00678 {
00679     NS_ASSERTION(!mCardList.IsInitialized() || !mCardList.GetEntry(aCard),
00680                  "Replacing?");
00681 
00682     if (!mCardList.IsInitialized() && !mCardList.Init()) {
00683         return NS_ERROR_OUT_OF_MEMORY;
00684     }
00685 
00686     mCardList.PutEntry(aCard);
00687     return aSession->NotifyDirectoryItemAdded(this, aCard);
00688 }
00689 
00690 nsresult
00691 nsAbOSXDirectory::UnassertDirectory(nsIAddrBookSession *aSession,
00692                                     nsIAbDirectory *aDirectory)
00693 {
00694     NS_ASSERTION(m_AddressList->IndexOf(aDirectory) >= 0, "Not found?");
00695 
00696     nsresult rv = m_AddressList->RemoveElement(aDirectory);
00697     NS_ENSURE_SUCCESS(rv, rv);
00698 
00699     return aSession->NotifyDirectoryItemDeleted(this, aDirectory);
00700 }
00701 
00702 nsresult
00703 nsAbOSXDirectory::UnassertCard(nsIAddrBookSession *aSession,
00704                                nsIAbCard *aCard)
00705 {
00706     NS_ASSERTION(mCardList.GetEntry(aCard), "Not found?");
00707 
00708     mCardList.RemoveEntry(aCard);
00709     return aSession->NotifyDirectoryItemDeleted(this, aCard);
00710 }
00711 
00712 NS_IMETHODIMP
00713 nsAbOSXDirectory::GetChildNodes(nsISimpleEnumerator **aNodes)
00714 {
00715     NS_ENSURE_ARG_POINTER(aNodes);
00716 
00717     // Queries don't have childnodes.
00718     if (mIsQueryURI || !m_AddressList) {
00719         return NS_NewEmptyEnumerator(aNodes);
00720     }
00721 
00722     return NS_NewArrayEnumerator(aNodes, m_AddressList);
00723 }
00724 
00725 NS_IMETHODIMP
00726 nsAbOSXDirectory::GetChildCards(nsIEnumerator **aCards)
00727 {
00728     NS_ENSURE_ARG_POINTER(aCards);
00729 
00730     ABAddressBook *addressBook = [ABAddressBook sharedAddressBook];
00731 
00732     nsresult rv;
00733     NSArray *cards;
00734     if (mIsQueryURI) {
00735         nsCOMPtr<nsIAbBooleanExpression> expression;
00736         rv = nsAbQueryStringToExpression::Convert(mQueryString.get(),
00737                                                   getter_AddRefs(expression));
00738         NS_ENSURE_SUCCESS(rv, rv);
00739 
00740         PRBool canHandle = !m_IsMailList && Search(expression, &cards);
00741         if (!canHandle) {
00742             return FallbackSearch(expression, aCards);
00743         }
00744     }
00745     else {
00746         if (m_IsMailList) {
00747             ABGroup *group = (ABGroup*)[addressBook recordForUniqueId:[NSString stringWithUTF8String:nsCAutoString(Substring(mURINoQuery, 21)).get()]];
00748             cards = [[group members] arrayByAddingObjectsFromArray:[group subgroups]];
00749         }
00750         else {
00751             cards = [[addressBook people] arrayByAddingObjectsFromArray:[addressBook groups]];
00752         }
00753     }
00754 
00755     // Fill the results array and update the card list
00756     // Also update the address list and notify any changes.
00757     unsigned int nbCards = [cards count];
00758     if (nbCards > 0) {
00759         if (mCardList.IsInitialized()) {
00760             mCardList.Clear();
00761         }
00762         else if (!mCardList.Init()) {
00763             return NS_ERROR_OUT_OF_MEMORY;
00764         }
00765     }
00766 
00767     nsCOMPtr<nsISupportsArray> cardList;
00768     rv = NS_NewISupportsArray(getter_AddRefs(cardList));
00769     NS_ENSURE_SUCCESS(rv, rv);
00770 
00771     unsigned int i;
00772     nsCOMPtr<nsIAbCard> card;
00773     for (i = 0; i < nbCards; ++i) {
00774         rv = ConvertToCard(gRDFService, [cards objectAtIndex:i],
00775                            getter_AddRefs(card));
00776         NS_ENSURE_SUCCESS(rv, rv);
00777 
00778         rv = cardList->AppendElement(card);
00779         NS_ENSURE_SUCCESS(rv, rv);
00780 
00781         mCardList.PutEntry(card);
00782     }
00783 
00784     return GetEnumerator(cardList, aCards);
00785 }
00786 
00787 NS_IMETHODIMP
00788 nsAbOSXDirectory::HasCard(nsIAbCard *aCard, PRBool *aHasCard)
00789 {
00790     NS_ENSURE_ARG_POINTER(aCard);
00791     NS_ENSURE_ARG_POINTER(aHasCard);
00792 
00793     *aHasCard = mCardList.IsInitialized() && mCardList.GetEntry(aCard);
00794 
00795     return NS_OK;
00796 }
00797 
00798 NS_IMETHODIMP
00799 nsAbOSXDirectory::HasDirectory(nsIAbDirectory *aDirectory,
00800                                PRBool *aHasDirectory)
00801 {
00802     NS_ENSURE_ARG_POINTER(aDirectory);
00803     NS_ENSURE_ARG_POINTER(aHasDirectory);
00804 
00805     *aHasDirectory = m_AddressList && m_AddressList->IndexOf(aDirectory) >= 0;
00806 
00807     return NS_OK;
00808 }
00809 
00810 nsresult
00811 nsAbOSXDirectory::OnSearchFinished(PRInt32 aResult)
00812 {
00813     return NS_OK;
00814 }
00815 
00816 nsresult
00817 nsAbOSXDirectory::OnSearchFoundCard(nsIAbCard *aCard)
00818 {
00819     nsresult rv;
00820     if (!m_AddressList) {
00821         rv = NS_NewISupportsArray(getter_AddRefs(m_AddressList));
00822         NS_ENSURE_SUCCESS(rv, rv);
00823     }
00824 
00825     if (!mCardList.IsInitialized() && !mCardList.Init()) {
00826         return NS_ERROR_OUT_OF_MEMORY;
00827     }
00828 
00829     rv = m_AddressList->AppendElement(aCard);
00830     NS_ENSURE_SUCCESS(rv, rv);
00831 
00832     mCardList.PutEntry(aCard);
00833 
00834     return NS_OK;
00835 }
00836 
00837 nsresult
00838 nsAbOSXDirectory::FallbackSearch(nsIAbBooleanExpression *aExpression,
00839                                  nsIEnumerator **aCards)
00840 {
00841     nsresult rv;
00842 
00843     if (mCardList.IsInitialized()) {
00844         mCardList.Clear();
00845     }
00846     else if (!mCardList.Init()) {
00847         return NS_ERROR_OUT_OF_MEMORY;
00848     }
00849 
00850     if (m_AddressList) {
00851         m_AddressList->Clear();
00852     }
00853     else {
00854         rv = NS_NewISupportsArray(getter_AddRefs(m_AddressList));
00855         NS_ENSURE_SUCCESS(rv, rv);
00856     }
00857 
00858     nsCOMPtr<nsIAbDirectoryQueryArguments> arguments =
00859         do_CreateInstance(NS_ABDIRECTORYQUERYARGUMENTS_CONTRACTID, &rv);
00860     NS_ENSURE_SUCCESS(rv, rv);
00861 
00862     rv = arguments->SetExpression(aExpression);
00863     NS_ENSURE_SUCCESS(rv, rv);
00864 
00865     // Set the return properties to
00866     // return nsIAbCard interfaces
00867     const char* property = "card:nsIAbCard";
00868     rv = arguments->SetReturnProperties(1, &property);
00869     NS_ENSURE_SUCCESS(rv, rv);
00870 
00871     // Don't search the subdirectories. If the current directory is a mailing
00872     // list, it won't have any subdirectories. If the current directory is an
00873     // addressbook, searching both it and the subdirectories (the mailing
00874     // lists), will yield duplicate results because every entry in a mailing
00875     // list will be an entry in the parent addressbook.
00876     rv = arguments->SetQuerySubDirectories(PR_FALSE);
00877     NS_ENSURE_SUCCESS(rv, rv);
00878 
00879     // Set the the query listener
00880     nsCOMPtr<nsIAbDirectoryQueryResultListener> queryListener =
00881         new nsAbDirSearchListener(this);
00882     if (!queryListener) {
00883         return NS_ERROR_OUT_OF_MEMORY;
00884     }
00885 
00886     // Get the directory without the query
00887     nsCOMPtr<nsIRDFResource> resource;
00888     rv = gRDFService->GetResource(mURINoQuery, getter_AddRefs(resource));
00889     NS_ENSURE_SUCCESS(rv, rv);
00890 
00891     nsCOMPtr<nsIAbDirectory> directory = do_QueryInterface(resource, &rv);
00892     NS_ENSURE_SUCCESS(rv, rv);
00893 
00894     // Initiate the proxy query with the no query directory
00895     nsCOMPtr<nsIAbDirectoryQueryProxy> queryProxy = 
00896         do_CreateInstance(NS_ABDIRECTORYQUERYPROXY_CONTRACTID, &rv);
00897     NS_ENSURE_SUCCESS(rv, rv);
00898 
00899     rv = queryProxy->Initiate(directory);
00900     NS_ENSURE_SUCCESS(rv, rv);
00901 
00902     PRInt32 context = 0;
00903     rv = queryProxy->DoQuery(arguments, queryListener, -1, 0, &context);
00904     NS_ENSURE_SUCCESS(rv, rv);
00905 
00906     return GetEnumerator(m_AddressList, aCards);
00907 }