/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla 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/MPL/
 *
 * 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 the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Seth Spitzer <sspitzer@netscape.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "nsSubscribeDataSource.h"

#include "nsIRDFService.h"
#include "nsRDFCID.h"
#include "nsIComponentManager.h"
#include "rdf.h"
#include "nsIServiceManager.h"
#include "nsEnumeratorUtils.h"
#include "nsStringGlue.h"
#include "nsCOMPtr.h"
#include "nsIMsgFolder.h"
#include "nsIMsgIncomingServer.h"
#include "nsCOMArray.h"
#include "nsArrayEnumerator.h"
#include "nsServiceManagerUtils.h"
#include "nsComponentManagerUtils.h"
#include "nsMsgUtils.h"

static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);

// this is used for notification of observers using nsVoidArray
typedef struct _nsSubscribeNotification {
  nsIRDFDataSource *datasource;
  nsIRDFResource *subject;
  nsIRDFResource *property;
  nsIRDFNode *object;
} nsSubscribeNotification;

nsSubscribeDataSource::nsSubscribeDataSource()
{
}

nsSubscribeDataSource::~nsSubscribeDataSource()
{
}

NS_IMPL_THREADSAFE_ADDREF(nsSubscribeDataSource)
NS_IMPL_THREADSAFE_RELEASE(nsSubscribeDataSource)

NS_IMPL_QUERY_INTERFACE2(nsSubscribeDataSource, nsIRDFDataSource, nsISubscribeDataSource) 

nsresult
nsSubscribeDataSource::Init()
{
    nsresult rv;

    mRDFService = do_GetService(kRDFServiceCID, &rv);
    NS_ASSERTION(NS_SUCCEEDED(rv) && mRDFService, "failed to get rdf service");
    NS_ENSURE_SUCCESS(rv,rv);
    if (!mRDFService) return NS_ERROR_FAILURE;

    rv = mRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "child"),
                                  getter_AddRefs(kNC_Child));
    NS_ENSURE_SUCCESS(rv,rv);

    rv = mRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Name"),
                                  getter_AddRefs(kNC_Name));
    NS_ENSURE_SUCCESS(rv,rv);

    rv = mRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "LeafName"),
                                  getter_AddRefs(kNC_LeafName));
    NS_ENSURE_SUCCESS(rv,rv);

    rv = mRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Subscribed"),
                                  getter_AddRefs(kNC_Subscribed));
    NS_ENSURE_SUCCESS(rv,rv);

    rv = mRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Subscribable"),
                                  getter_AddRefs(kNC_Subscribable));
    NS_ENSURE_SUCCESS(rv,rv);

    rv = mRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "ServerType"),
                                  getter_AddRefs(kNC_ServerType));
    NS_ENSURE_SUCCESS(rv,rv);

    rv = mRDFService->GetLiteral(NS_LITERAL_STRING("true").get(),getter_AddRefs(kTrueLiteral));
    NS_ENSURE_SUCCESS(rv,rv);
  
    rv = mRDFService->GetLiteral(NS_LITERAL_STRING("false").get(),getter_AddRefs(kFalseLiteral));
    NS_ENSURE_SUCCESS(rv,rv);
	return NS_OK;
}

NS_IMETHODIMP 
nsSubscribeDataSource::GetURI(char * *aURI)
{
  if ((*aURI = strdup("rdf:subscribe")) == nsnull)
    return NS_ERROR_OUT_OF_MEMORY;
  else
    return NS_OK;
}

NS_IMETHODIMP 
nsSubscribeDataSource::GetSource(nsIRDFResource *property, nsIRDFNode *target, bool tv, nsIRDFResource **source)
{
    NS_PRECONDITION(property != nsnull, "null ptr");
    if (! property)
        return NS_ERROR_NULL_POINTER;

    NS_PRECONDITION(target != nsnull, "null ptr");
    if (! target)
        return NS_ERROR_NULL_POINTER;

    NS_PRECONDITION(source != nsnull, "null ptr");
    if (! source)
        return NS_ERROR_NULL_POINTER;

    *source = nsnull;
    return NS_RDF_NO_VALUE;
}

NS_IMETHODIMP
nsSubscribeDataSource::GetTarget(nsIRDFResource *source,
                                nsIRDFResource *property,
                                bool tv,
                                nsIRDFNode **target /* out */)
{
	nsresult rv = NS_RDF_NO_VALUE;

	NS_PRECONDITION(source != nsnull, "null ptr");
	if (! source)
		return NS_ERROR_NULL_POINTER;

	NS_PRECONDITION(property != nsnull, "null ptr");
	if (! property)
		return NS_ERROR_NULL_POINTER;

	NS_PRECONDITION(target != nsnull, "null ptr");
	if (! target)
		return NS_ERROR_NULL_POINTER;

	*target = nsnull;

	// we only have positive assertions in the subscribe data source.
	if (! tv) return NS_RDF_NO_VALUE;

    nsCOMPtr<nsISubscribableServer> server;
    nsCString relativePath;
    rv = GetServerAndRelativePathFromResource(source, getter_AddRefs(server), getter_Copies(relativePath));
    if (NS_FAILED(rv) || !server)
        return NS_RDF_NO_VALUE;

    if (property == kNC_Name.get()) {
        nsCOMPtr<nsIRDFLiteral> name;
        rv = mRDFService->GetLiteral(NS_ConvertUTF8toUTF16(relativePath).get(),
                                     getter_AddRefs(name));
        NS_ENSURE_SUCCESS(rv,rv);

        if (!name) rv = NS_RDF_NO_VALUE;
        if (rv == NS_RDF_NO_VALUE) return(rv);
        return name->QueryInterface(NS_GET_IID(nsIRDFNode), (void**) target);
    }
    else if (property == kNC_Child.get()) {
        nsCString childUri;
        rv = server->GetFirstChildURI(relativePath, childUri);
        if (NS_FAILED(rv)) return NS_RDF_NO_VALUE;
        if (childUri.IsEmpty()) return NS_RDF_NO_VALUE;

        nsCOMPtr <nsIRDFResource> childResource;
        rv = mRDFService->GetResource(childUri, getter_AddRefs(childResource));
        NS_ENSURE_SUCCESS(rv,rv);
        
        return childResource->QueryInterface(NS_GET_IID(nsIRDFNode), (void**) target);
    }
    else if (property == kNC_Subscribed.get()) {
        bool isSubscribed;
        rv = server->IsSubscribed(relativePath, &isSubscribed);
        NS_ENSURE_SUCCESS(rv,rv);
    
        NS_IF_ADDREF(*target = (isSubscribed ? kTrueLiteral : kFalseLiteral));
        return NS_OK;
    }
    else if (property == kNC_Subscribable.get()) {
        bool isSubscribable;
        rv = server->IsSubscribable(relativePath, &isSubscribable);
        NS_ENSURE_SUCCESS(rv,rv);
        
        NS_IF_ADDREF(*target = (isSubscribable ? kTrueLiteral : kFalseLiteral));
        return NS_OK;
    }
    else if (property == kNC_ServerType.get()) {
        nsCString serverTypeStr;
        rv = GetServerType(server, serverTypeStr);
        NS_ENSURE_SUCCESS(rv,rv);

        nsCOMPtr<nsIRDFLiteral> serverType;
        rv = mRDFService->GetLiteral(NS_ConvertASCIItoUTF16(serverTypeStr).get(),
                                     getter_AddRefs(serverType));
        NS_ENSURE_SUCCESS(rv,rv);

        if (!serverType)
          rv = NS_RDF_NO_VALUE;
        if (rv == NS_RDF_NO_VALUE) 
          return rv;
        return serverType->QueryInterface(NS_GET_IID(nsIRDFNode), (void**) target);
    }
    else if (property == kNC_LeafName.get()) {
        nsString leafNameStr;
        rv = server->GetLeafName(relativePath, leafNameStr); 
        NS_ENSURE_SUCCESS(rv,rv);
   
        nsCOMPtr<nsIRDFLiteral> leafName;
        rv = mRDFService->GetLiteral(leafNameStr.get(), getter_AddRefs(leafName));
        NS_ENSURE_SUCCESS(rv,rv);

        if (!leafName)
          rv = NS_RDF_NO_VALUE;
        if (rv == NS_RDF_NO_VALUE)
          return rv;
        return leafName->QueryInterface(NS_GET_IID(nsIRDFNode), (void**) target);
    }
    else {
        // do nothing
    }

  return(NS_RDF_NO_VALUE);
}

NS_IMETHODIMP
nsSubscribeDataSource::GetTargets(nsIRDFResource *source,
				nsIRDFResource *property,
				bool tv,
				nsISimpleEnumerator **targets /* out */)
{
	nsresult rv = NS_OK;

	NS_PRECONDITION(source != nsnull, "null ptr");
	if (! source)
		return NS_ERROR_NULL_POINTER;

	NS_PRECONDITION(property != nsnull, "null ptr");
	if (! property)
		return NS_ERROR_NULL_POINTER;

	NS_PRECONDITION(targets != nsnull, "null ptr");
	if (! targets)
		return NS_ERROR_NULL_POINTER;

    *targets = nsnull;

	// we only have positive assertions in the subscribe data source.
	if (!tv) return NS_RDF_NO_VALUE;

    nsCOMPtr<nsISubscribableServer> server;
    nsCString relativePath;  // UTF-8

    rv = GetServerAndRelativePathFromResource(source, getter_AddRefs(server), getter_Copies(relativePath));
    if (NS_FAILED(rv) || !server) {
	    return NS_NewEmptyEnumerator(targets);
    }

    if (property == kNC_Child.get()) {
        rv = server->GetChildren(relativePath, targets);
        if (NS_FAILED(rv)) {
            return NS_NewEmptyEnumerator(targets);
        }
        return rv;
    }
    else if (property == kNC_LeafName.get()) {
        nsString leafNameStr;
        rv = server->GetLeafName(relativePath, leafNameStr);
        NS_ENSURE_SUCCESS(rv,rv);
    
        nsCOMPtr<nsIRDFLiteral> leafName;
        rv = mRDFService->GetLiteral(leafNameStr.get(), getter_AddRefs(leafName));
        NS_ENSURE_SUCCESS(rv,rv);

        return NS_NewSingletonEnumerator(targets, leafName);
    }
    else if (property == kNC_Subscribed.get()) {
        bool isSubscribed;
        rv = server->IsSubscribed(relativePath, &isSubscribed);
        NS_ENSURE_SUCCESS(rv,rv);

        return NS_NewSingletonEnumerator(targets,
                 isSubscribed ? kTrueLiteral : kFalseLiteral);
    }
    else if (property == kNC_Subscribable.get()) {
        bool isSubscribable;
        rv = server->IsSubscribable(relativePath, &isSubscribable);
        NS_ENSURE_SUCCESS(rv,rv);

        return NS_NewSingletonEnumerator(targets,
                 isSubscribable ? kTrueLiteral : kFalseLiteral);
    }
    else if (property == kNC_Name.get()) {
        nsCOMPtr<nsIRDFLiteral> name;
        rv = mRDFService->GetLiteral(NS_ConvertUTF8toUTF16(relativePath).get(),
                                     getter_AddRefs(name));
        NS_ENSURE_SUCCESS(rv,rv);

        return NS_NewSingletonEnumerator(targets, name);
    }
    else if (property == kNC_ServerType.get()) {
        nsCString serverTypeStr;
        rv = GetServerType(server, serverTypeStr);
        NS_ENSURE_SUCCESS(rv,rv);

        nsCOMPtr<nsIRDFLiteral> serverType;
        rv = mRDFService->GetLiteral(NS_ConvertASCIItoUTF16(serverTypeStr).get(),
                                     getter_AddRefs(serverType));
        NS_ENSURE_SUCCESS(rv,rv);

        return NS_NewSingletonEnumerator(targets, serverType);
    }
    else {
        // do nothing
    }

	return NS_NewEmptyEnumerator(targets);
}

NS_IMETHODIMP
nsSubscribeDataSource::Assert(nsIRDFResource *source,
                       nsIRDFResource *property,
                       nsIRDFNode *target,
                       bool tv)
{
	return NS_RDF_ASSERTION_REJECTED;
}



NS_IMETHODIMP
nsSubscribeDataSource::Unassert(nsIRDFResource *source,
                         nsIRDFResource *property,
                         nsIRDFNode *target)
{
  return NS_RDF_ASSERTION_REJECTED;
}



NS_IMETHODIMP
nsSubscribeDataSource::Change(nsIRDFResource* aSource,
                              nsIRDFResource* aProperty,
                              nsIRDFNode* aOldTarget,
                              nsIRDFNode* aNewTarget)
{
  return NS_RDF_ASSERTION_REJECTED;
}



NS_IMETHODIMP
nsSubscribeDataSource::Move(nsIRDFResource* aOldSource,
                            nsIRDFResource* aNewSource,
                            nsIRDFResource* aProperty,
                            nsIRDFNode* aTarget)
{
  return NS_RDF_ASSERTION_REJECTED;
}

nsresult
nsSubscribeDataSource::GetServerType(nsISubscribableServer *server, nsACString& serverType)
{
  NS_ENSURE_ARG_POINTER(server);
  nsresult rv;
  nsCOMPtr<nsIMsgIncomingServer> incomingServer(do_QueryInterface(server, &rv));
  NS_ENSURE_SUCCESS(rv,rv);
  return incomingServer->GetType(serverType);
}

nsresult
nsSubscribeDataSource::GetServerAndRelativePathFromResource(nsIRDFResource *source, nsISubscribableServer **server, char **relativePath)
{
    nsresult rv = NS_OK;

    const char *sourceURI = nsnull;
    rv = source->GetValueConst(&sourceURI);
    NS_ENSURE_SUCCESS(rv,rv);

    nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(source, &rv));
    // we expect this to fail sometimes, so don't assert
    if (NS_FAILED(rv))
      return rv;

    nsCOMPtr<nsIMsgIncomingServer> incomingServer;
    rv = folder->GetServer(getter_AddRefs(incomingServer));
    NS_ENSURE_SUCCESS(rv,rv);

    rv = incomingServer->QueryInterface(NS_GET_IID(nsISubscribableServer), (void**)server);
    NS_ENSURE_SUCCESS(rv,rv);

    nsCString serverURI;
    rv = incomingServer->GetServerURI(serverURI);
    NS_ENSURE_SUCCESS(rv,rv);
 
    PRUint32 serverURILen = serverURI.Length();
    if (serverURILen == strlen(sourceURI))
      *relativePath = nsnull;
    else {
      // XXX : perhaps, have to unescape before returning 
      *relativePath = strdup(sourceURI + serverURILen + 1);
      if (!*relativePath)
        return NS_ERROR_OUT_OF_MEMORY;
    }

    return NS_OK;
}

NS_IMETHODIMP
nsSubscribeDataSource::HasAssertion(nsIRDFResource *source,
                             nsIRDFResource *property,
                             nsIRDFNode *target,
                             bool tv,
                             bool *hasAssertion /* out */)
{
    nsresult rv = NS_OK;

	NS_PRECONDITION(source != nsnull, "null ptr");
	if (! source)
		return NS_ERROR_NULL_POINTER;

	NS_PRECONDITION(property != nsnull, "null ptr");
	if (! property)
		return NS_ERROR_NULL_POINTER;

	NS_PRECONDITION(target != nsnull, "null ptr");
	if (! target)
		return NS_ERROR_NULL_POINTER;

	NS_PRECONDITION(hasAssertion != nsnull, "null ptr");
	if (! hasAssertion)
		return NS_ERROR_NULL_POINTER;

	*hasAssertion = PR_FALSE;

  // we only have positive assertions in the subscribe data source.
	if (!tv) return NS_OK;

	if (property == kNC_Child.get()) {
    nsCOMPtr<nsISubscribableServer> server;
    nsCString relativePath;

    rv = GetServerAndRelativePathFromResource(source, getter_AddRefs(server), getter_Copies(relativePath));
    if (NS_FAILED(rv) || !server) {
        *hasAssertion = PR_FALSE;
        return NS_OK;
    }

        // not everything has children
        rv = server->HasChildren(relativePath, hasAssertion);
        NS_ENSURE_SUCCESS(rv,rv);
    }
    else if (property == kNC_Name.get()) {
        // everything has a name
        *hasAssertion = PR_TRUE;
    }
    else if (property == kNC_LeafName.get()) {
        // everything has a leaf name
        *hasAssertion = PR_TRUE;
    }
    else if (property == kNC_Subscribed.get()) {
        // everything is subscribed or not
        *hasAssertion = PR_TRUE;
    }
    else if (property == kNC_Subscribable.get()) {
        // everything is subscribable or not
        *hasAssertion = PR_TRUE;
    }
    else if (property == kNC_ServerType.get()) {
        // everything has a server type
        *hasAssertion = PR_TRUE;
    }
    else {
        // do nothing
    }

	return NS_OK;
}


NS_IMETHODIMP 
nsSubscribeDataSource::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *result)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP 
nsSubscribeDataSource::HasArcOut(nsIRDFResource *source, nsIRDFResource *aArc, bool *result)
{
    nsresult rv = NS_OK;

    nsCOMPtr<nsISubscribableServer> server;
    nsCString relativePath;

    if (aArc == kNC_Child.get()) {
    rv = GetServerAndRelativePathFromResource(source, getter_AddRefs(server), getter_Copies(relativePath));
    if (NS_FAILED(rv) || !server) {
	    *result = PR_FALSE;
        return NS_OK;
    }

        bool hasChildren = false;
        rv = server->HasChildren(relativePath, &hasChildren);
        NS_ENSURE_SUCCESS(rv,rv);
        *result = hasChildren;
        return NS_OK;
    }
    else if ((aArc == kNC_Subscribed.get()) ||
             (aArc == kNC_Subscribable.get()) ||
             (aArc == kNC_LeafName.get()) ||
             (aArc == kNC_ServerType.get()) ||
             (aArc == kNC_Name.get())) {
        *result = PR_TRUE;
        return NS_OK;
    }

    *result = PR_FALSE;
    return NS_OK;
}


NS_IMETHODIMP
nsSubscribeDataSource::ArcLabelsIn(nsIRDFNode *node,
                            nsISimpleEnumerator ** labels /* out */)
{
	return NS_ERROR_NOT_IMPLEMENTED;
}



NS_IMETHODIMP
nsSubscribeDataSource::ArcLabelsOut(nsIRDFResource *source,
				   nsISimpleEnumerator **labels /* out */)
{
    nsresult rv = NS_OK;

    NS_PRECONDITION(source != nsnull, "null ptr");
    if (! source)
	return NS_ERROR_NULL_POINTER;

    NS_PRECONDITION(labels != nsnull, "null ptr");
    if (! labels)
	return NS_ERROR_NULL_POINTER;

    nsCOMPtr<nsISubscribableServer> server;
    nsCString relativePath;

    rv = GetServerAndRelativePathFromResource(source, getter_AddRefs(server), getter_Copies(relativePath));
    if (NS_FAILED(rv) || !server) {
        return NS_NewEmptyEnumerator(labels);
    }

    bool hasChildren = false;
    rv = server->HasChildren(relativePath, &hasChildren);
    NS_ENSURE_SUCCESS(rv,rv);

    // Initialise with the number of items below, to save reallocating on each
    // addition.
    nsCOMArray<nsIRDFResource> array(hasChildren ? 6 : 5);

    array.AppendObject(kNC_Subscribed);
    array.AppendObject(kNC_Subscribable);
    array.AppendObject(kNC_Name);
    array.AppendObject(kNC_ServerType);
    array.AppendObject(kNC_LeafName);

    if (hasChildren) {
        array.AppendObject(kNC_Child);
    }

    return NS_NewArrayEnumerator(labels, array);
}

NS_IMETHODIMP
nsSubscribeDataSource::GetAllResources(nsISimpleEnumerator** aCursor)
{
	NS_NOTYETIMPLEMENTED("sorry!");
	return NS_ERROR_NOT_IMPLEMENTED;
}



NS_IMETHODIMP
nsSubscribeDataSource::AddObserver(nsIRDFObserver *n)
{
    NS_PRECONDITION(n != nsnull, "null ptr");
    if (! n)
        return NS_ERROR_NULL_POINTER;

	if (! mObservers)
	{
		nsresult rv;
		rv = NS_NewISupportsArray(getter_AddRefs(mObservers));
		if (NS_FAILED(rv)) return rv;
	}
	mObservers->AppendElement(n);
	return NS_OK;
}


NS_IMETHODIMP
nsSubscribeDataSource::RemoveObserver(nsIRDFObserver *n)
{
    NS_PRECONDITION(n != nsnull, "null ptr");
    if (! n)
        return NS_ERROR_NULL_POINTER;

	if (! mObservers)
		return NS_OK;

	mObservers->RemoveElement(n);
	return NS_OK;
}

NS_IMETHODIMP
nsSubscribeDataSource::GetHasObservers(bool *hasObservers)
{
    nsresult rv = NS_OK;
    NS_ASSERTION(hasObservers, "null ptr");
    if (!hasObservers) return NS_ERROR_NULL_POINTER;
    
    if (!mObservers) {
        *hasObservers = PR_FALSE;
        return NS_OK;
    }
    
    PRUint32 count = 0;
    rv = mObservers->Count(&count);
    NS_ENSURE_SUCCESS(rv,rv);

    *hasObservers = (count > 0);
    return NS_OK;
}

NS_IMETHODIMP
nsSubscribeDataSource::GetAllCmds(nsIRDFResource* source,
                                     nsISimpleEnumerator/*<nsIRDFResource>*/** commands)
{
	return(NS_NewEmptyEnumerator(commands));
}

NS_IMETHODIMP
nsSubscribeDataSource::IsCommandEnabled(nsISupportsArray/*<nsIRDFResource>*/* aSources,
                                       nsIRDFResource*   aCommand,
                                       nsISupportsArray/*<nsIRDFResource>*/* aArguments,
                                       bool* aResult)
{
	return(NS_ERROR_NOT_IMPLEMENTED);
}



NS_IMETHODIMP
nsSubscribeDataSource::DoCommand(nsISupportsArray/*<nsIRDFResource>*/* aSources,
                                nsIRDFResource*   aCommand,
                                nsISupportsArray/*<nsIRDFResource>*/* aArguments)
{
	return(NS_ERROR_NOT_IMPLEMENTED);
}



NS_IMETHODIMP
nsSubscribeDataSource::BeginUpdateBatch()
{
        return NS_OK;
}



NS_IMETHODIMP
nsSubscribeDataSource::EndUpdateBatch()
{
        return NS_OK;
}



NS_IMETHODIMP 
nsSubscribeDataSource::GetSources(nsIRDFResource *aProperty, nsIRDFNode *aTarget, bool aTruthValue, nsISimpleEnumerator **_retval)
{
  NS_ASSERTION(PR_FALSE, "Not implemented");
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP
nsSubscribeDataSource::NotifyObservers(nsIRDFResource *subject,
                                                nsIRDFResource *property,
                                                nsIRDFNode *object,
                                                bool assert, bool change)
{
    NS_ASSERTION(!(change && assert),
                 "Can't change and assert at the same time!\n");

    if(mObservers)
    {
        nsSubscribeNotification note = { this, subject, property, object };
        if(change)
            mObservers->EnumerateForwards(changeEnumFunc, &note);
        else if (assert)
            mObservers->EnumerateForwards(assertEnumFunc, &note);
        else
            mObservers->EnumerateForwards(unassertEnumFunc, &note);
  }
    return NS_OK;
}

bool
nsSubscribeDataSource::changeEnumFunc(nsISupports *aElement, void *aData)
{
  nsSubscribeNotification* note = (nsSubscribeNotification*)aData;
  nsIRDFObserver* observer = (nsIRDFObserver *)aElement;

  observer->OnChange(note->datasource,
                     note->subject,
                     note->property,
                     nsnull, note->object);
  return PR_TRUE;
}

bool
nsSubscribeDataSource::assertEnumFunc(nsISupports *aElement, void *aData)
{
  nsSubscribeNotification* note = (nsSubscribeNotification*)aData;
  nsIRDFObserver* observer = (nsIRDFObserver *)aElement;

  observer->OnAssert(note->datasource,
                     note->subject,
                     note->property,
                     note->object);
  return PR_TRUE;
}

bool
nsSubscribeDataSource::unassertEnumFunc(nsISupports *aElement, void *aData)
{
  nsSubscribeNotification* note = (nsSubscribeNotification*)aData;
  nsIRDFObserver* observer = (nsIRDFObserver *)aElement;

  observer->OnUnassert(note->datasource,
                       note->subject,
                       note->property,
                       note->object);
  return PR_TRUE;
}
