/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/* 
   This file (which should probably be called rdf.c) implements the
   core rdf query mechanism.
   For more information on this file, contact rjc or guha 
   For more information on RDF, look at the RDF section of www.mozilla.org
*/

#include "core.h"

#ifdef DEBUG_guha
#define SMART_MAIL 1
#endif


	/* globals */
RDFL		gAllDBs = 0;



RDFT
getTranslator (char* url)
{
  RDFT ans = PL_HashTableLookup(dataSourceHash, url);
  if (ans) return ans;
  if (startsWith(url, "rdf:localStore")) {
    ans = MakeLocalStore(url);
  } else if (startsWith(url, "rdf:remoteStore")) {
    ans =  MakeRemoteStore(url);
  } else if (startsWith(url, "rdf:bookmarks")) {
    ans = MakeBMKStore(url);
  } else if (strstr(url, ".rdf") || strstr(url, ".RDF") || 
		     strstr(url, ".mcf") || strstr(url, ".MCF")) {
    ans = MakeFileDB(url);
  } else if (startsWith("rdf:columns", url)) {
    ans = MakeColumnStore (url);
  } else if (startsWith("rdf:ht", url) || startsWith("rdf:scook", url)) {
    ans = MakeSCookDB(url);
#ifdef XPCOM_XXX
  } else if (startsWith("rdf:CookieStore", url)) {
    ans = MakeCookieStore(url);
#endif
  } else if (startsWith("http://", url)) {
	  ans = MakeFileDB(url); 
  } else {
    ans = NULL;
  }
  if (ans) PL_HashTableAdd(dataSourceHash, ans->url, ans);
  return ans;
}



PR_PUBLIC_API(RDF)
RDF_GetDB (const char** dataSources) {
  int32 n = 0;
  int32 m = 0;
  char* next ;
  RDF r = (RDF) getMem(sizeof(struct RDF_DBStruct)) ;
  RDFL nrl = (RDFL)getMem(sizeof(struct RDF_ListStruct));
  while (*(dataSources + n++)) {} 
  r->translators = (RDFT*)getMem((n) * sizeof(RDFT));
  n = 0;
  while ((next = (char*) *(dataSources + n)) != NULL) {
    RDFL rl = (RDFL)getMem(sizeof(struct RDF_ListStruct));
    r->translators[m] = getTranslator(next);
    if (r->translators[m] != NULL) { 
		rl->rdf = r;
		rl->next = r->translators[m]->rdf;
		r->translators[m]->rdf = rl;
		m++;
	} else {
      freeMem(rl);
      /*      r->numTranslators = m;
      r->translatorArraySize = n;
      RDF_ReleaseDB(r);
      return NULL; */
	}
    n++;
  }
  r->numTranslators = m;
  r->translatorArraySize = n;
  nrl->rdf = r;
  nrl->next = gAllDBs;
  gAllDBs = nrl;
  return r;
}



PR_PUBLIC_API(RDFT)
RDF_AddDataSource(RDF rdf, char* dataSource)
{
  RDFT newDB;
  int32 n = 0;
  RDFT next;
  while (((next = rdf->translators[n++]) != NULL) && (n < rdf->numTranslators)) {
    if (RDF_STRCMP(next->url, dataSource) == 0) return next;
  }
  if (rdf->numTranslators >= rdf->translatorArraySize) {
    RDFT* tmp = (RDFT*)getMem((rdf->numTranslators+5)*(sizeof(RDFT)));
    memcpy(tmp, rdf->translators, (rdf->numTranslators * sizeof(RDFT)));
    rdf->translatorArraySize = rdf->numTranslators + 5;
    freeMem(rdf->translators);
    rdf->translators = tmp;
  }
  newDB = getTranslator(dataSource);
  if (!newDB) {
    return NULL;  
  } else {
    RDFL rl = (RDFL)getMem(sizeof(struct RDF_ListStruct));
    RDFT tr = rdf->translators[rdf->numTranslators];
    rl->rdf = rdf;
    rl->next = newDB->rdf;
    newDB->rdf = rl;
    rdf->translators[rdf->numTranslators] = newDB;
    rdf->numTranslators++;
    return newDB;
  }	
}



PR_PUBLIC_API(RDF_Error)
RDF_ReleaseDataSource(RDF rdf, RDFT dataSource)
{
  RDFT* temp = (RDFT*)getMem((rdf->numTranslators-1)*(sizeof(RDFT)));
  int32 m = 0;
  int32 n= 0;
  RDFT next;
  while ((next = rdf->translators[n++]) != NULL) {
    if (next != dataSource) {
      *(temp + m++) = (RDFT) next;
    }
  }
  memset(rdf->translators, '\0', sizeof(RDFT) * rdf->numTranslators);
  memcpy(rdf->translators, temp, sizeof(RDFT) * (rdf->numTranslators -1));
  rdf->numTranslators--;
  dataSource->rdf = deleteFromRDFList(dataSource->rdf, rdf);
  if ((dataSource->rdf == NULL) && (dataSource->destroy != NULL)) {
      PL_HashTableRemove(dataSourceHash,  dataSource->url); 
      (*dataSource->destroy)(dataSource);
  }
  return 0;
}



RDFL
deleteFromRDFList (RDFL xrl, RDF db)
{
  RDFL rl = xrl;
  RDFL prev = rl;
  while (rl) {
      if (rl->rdf == db) {
        if (prev == rl) {
          RDFL ans = rl->next;
          freeMem(rl);
          return ans;
        } else {
          prev->next = rl->next;
          freeMem(rl);
          return xrl;
        }
      }
      prev = rl;
      rl   = rl->next;
  }
  return xrl;
}



void
disposeAllDBs ()
{
  while (gAllDBs) {
    RDF_ReleaseDB(gAllDBs->rdf);
  }
}



PR_PUBLIC_API(RDF_Error) 
RDF_ReleaseDB(RDF rdf)
{
  if (rdf != NULL) {
    uint32 n = 0;
    uint32 size = rdf->numTranslators;
    while (n < size) { 
	  RDFT rdft = (*((RDFT*)rdf->translators + n));
	  RDFL rlx ;
	  if (rdft) {
		  rlx =  rdft->rdf; 
		  rdft->rdf =  deleteFromRDFList(rlx, rdf); 
		  if (rdft->rdf == NULL) callExitRoutine(n, rdf);
	  }
      n++;
    }
    gAllDBs = deleteFromRDFList(gAllDBs, rdf);
  }
  
  freeMem(rdf->translators);
  freeMem(rdf);
  return noRDFErr;
}



RDF_Resource
addDep (RDF db, RDF_Resource u)
{
 
  if (db) {
    RDFL rl = u->rdf;
    while (rl) {
      if (rl->rdf == db) return u;
      rl = rl->next;
    }
    rl = (RDFL) getMem(sizeof(struct RDF_ListStruct));
    rl->rdf = db;
    rl->next = u->rdf;
    u->rdf = rl;
    return u;
  }
  return u;
}



PRBool
rdfassert(RDF rdf, RDF_Resource u, RDF_Resource  s, void* value, 
	      RDF_ValueType type,  PRBool tv)
{
   int32 size =  rdf->numTranslators;
   int32 n = size -1;
   
   /* XXX - what happens here if there is no local store? */     
   if ((size > 1) && (callHasAssertions(0, rdf, u, s, value, type, (!tv)))) {
     setLockedp(u, 1);
     callUnassert(0, rdf, u, s, value, type); 
     setLockedp(u, 0);
     if (RDF_HasAssertion(rdf, u, s, value, type, tv)) return 1;
   }
   while (n > -1) {
     if (callAssert(n, rdf, u, s, value, type, tv)) return 1; 
     n--; 
   }  
   return 0;
}


  

PR_PUBLIC_API(PRBool)
RDF_Assert (RDF rdf, RDF_Resource u, RDF_Resource  s, void* value, RDF_ValueType type)
{
	return rdfassert(rdf, u, s, value, type, 1);
}



PR_PUBLIC_API(PRBool)
RDF_AssertFalse (RDF rdf, RDF_Resource u, RDF_Resource  s, void* value, RDF_ValueType type)
{
	return rdfassert(rdf, u, s, value, type, 0);
}



PR_PUBLIC_API(PRBool)
RDF_CanAssert(RDF r, RDF_Resource u, RDF_Resource s, 
		    void* v, RDF_ValueType type)
{
	return true;
}



PR_PUBLIC_API(PRBool)
RDF_CanAssertFalse(RDF r, RDF_Resource u, RDF_Resource s, void* v, RDF_ValueType type)
{
	return true;
}



PR_PUBLIC_API(PRBool)
RDF_CanUnassert(RDF r, RDF_Resource u, RDF_Resource s, void* v, RDF_ValueType type)
{
	return true;
}



PR_PUBLIC_API(PRBool)
RDF_Unassert (RDF rdf, RDF_Resource u, RDF_Resource s, void* value, RDF_ValueType type)
{
  PRBool allok = 1;
  int32 size =  rdf->numTranslators;
  int32 n = 0;
  
  setLockedp(u, 1);
  while (allok && (n < size)) {
    if (callHasAssertions(n, rdf, u, s, value, type, 1)) {
      allok = callUnassert(n, rdf, u, s, value, type);
    }
    n++;
  }
  if (!allok) allok = callAssert(0, rdf, u, s, value, type, 0); 
  setLockedp(u, 0);
  return allok;
}



PRBool
containerIDp(char* id)
{
  return (endsWith(".rdf", id) || (endsWith(".mco", id)) || (endsWith(".mcf", id)) || 
           strstr(id, ".rdf#"));
}



char *
makeNewID ()
{
	PRTime		tm = PR_Now();
	char		*id;

#ifndef HAVE_LONG_LONG
	double		doubletm;
#endif /* !HAVE_LONG_LONG */


	id = (char *)getMem(ID_BUF_SIZE);

#ifdef HAVE_LONG_LONG
	PR_snprintf(id, ID_BUF_SIZE, "%1.0f", (double)tm);
#else
	LL_L2D(doubletm, tm);
	PR_snprintf(id, ID_BUF_SIZE, "%1.0f", doubletm);
#endif /* HAVE_LONG_LONG */

	/* prevent collisions by checking hash. */
	while (PL_HashTableLookup(resourceHash, id) != NULL)
	{
#ifdef HAVE_LONG_LONG
		tm = tm + (PR_MSEC_PER_SEC * 60); /* fix me - not sure what the increment should be */
		PR_snprintf(id, ID_BUF_SIZE, "%1.0f", (double)tm);
#else
		int64 incr;
		LL_I2L(incr, (PR_MSEC_PER_SEC * 60));
		LL_ADD(tm, tm, incr);
		LL_L2D(doubletm, tm);
		PR_snprintf(id, ID_BUF_SIZE, "%1.0f", doubletm);
#endif /* HAVE_LONG_LONG */
	}
	return (id);
}



PRBool
iscontainerp (RDF_Resource u)
{
  char* id = resourceID(u);
  if (containerIDp(id)) {
    return 1;
  } 
#ifdef MOZILLA_CLIENT  
  else if (startsWith("file:", id)) {
    return (endsWith("/", id) /* || fileDirectoryp(u) */);
  } else if (startsWith("ftp:", id) && (endsWith("/", id))) {
    return 1;
  } else if (startsWith("cache:container", id)) {
    return 1;
  } else if (startsWith("find:", id)) {
    return 1;
  }
#endif   
  else  return 0; 
} 



RDF_BT
resourceTypeFromID (char* id)
{
  if (endsWith(".rdf", id) || (strstr(id, ".rdf#"))) {
    return RDF_RT;
  } else if (endsWith(".mcf", id) || (strstr(id, ".mcf#"))) {
    return RDF_RT;
  } else if (endsWith(".mco", id)) {
    return RDF_RT;
  } else if (startsWith("file:", id))  {
    return LFS_RT;
  } else if (startsWith("nes:", id)) {
    return ES_RT;
  } else if (startsWith("ftp:", id)) {
    return FTP_RT;
  } else if (startsWith("cache:", id)) {
    return CACHE_RT;
  } else return 0;
}



RDF_Resource
specialUrlResource (char* id)
{
	if (RDF_STRCMP(id, "NC:PersonalToolbar") == 0)
		return RDFUtil_GetPTFolder();
	return NULL;
}



RDF_Resource
NewRDFResource (char* id)
{
	RDF_Resource	existing;
	
	if ((existing = (RDF_Resource)getMem(sizeof(struct RDF_ResourceStruct))) != NULL)
	{
		existing->url = copyString(id);
		PL_HashTableAdd(resourceHash, existing->url, existing);
	}
	return(existing);
}



RDF_Resource
QuickGetResource (char* id)
{
	RDF_Resource	existing;

	if ((existing = (RDF_Resource)PL_HashTableLookup(resourceHash, id)) == NULL)
	{
		existing = NewRDFResource(id);
	}
	return(existing);
}



PR_PUBLIC_API(RDF_Resource)
RDF_GetResource (RDF db, char* id, PRBool createp)
{
  char* nid = NULL;
  RDF_Resource existing = NULL;
  if (id == NULL) {
	if (!createp) {
		return NULL;
	} else {
		id = nid = makeNewID();
	}
  }
  existing = specialUrlResource(id);
  if (existing) return existing;
  existing = (RDF_Resource)PL_HashTableLookup(resourceHash, id);
  if (existing) return addDep(db, existing);
  if (!createp) return NULL;
  existing =  NewRDFResource(id);  
  setResourceType(existing, resourceTypeFromID(id));
  setContainerp(existing, iscontainerp(existing));
  if (nid) freeMem(nid);
  return addDep(db, existing); 
}



  
PR_PUBLIC_API (RDF_Error)
RDF_DeleteAllArcs (RDF rdf, RDF_Resource u)
{
  Assertion as, next;
  /*  if (u->locked) return -1; */
  as = u->rarg1;
  while (as != NULL) {
    next = as->next;
    remoteStoreRemove(gRemoteStore, as->u, as->s, as->value, as->type);
    as = next;
  }
  as = u->rarg2;
  while (as != NULL) {
    next = as->invNext;
    remoteStoreRemove(gRemoteStore, as->u, as->s, as->value, as->type);
    as = next;
  }
  return 0;
}

PR_PUBLIC_API(RDF_Error) 
RDF_Update(RDF rdf, RDF_Resource u) {
  int32 size = rdf->numTranslators;
  int32 n = 0;
  while (n < size) {
    callUpdateRoutine(n, rdf, u);
    n++;
  }
  return 0; 
}


PR_PUBLIC_API(PRBool)
RDF_HasAssertion (RDF rdf, RDF_Resource u, RDF_Resource s, 
		     void* v, RDF_ValueType type, PRBool tv)
{
  int32 size = rdf->numTranslators;
  int32 n = 0;
  if (callHasAssertions(0, rdf, u, s, v, type, !tv)) return 0;
  while (n < size) {
    if (callHasAssertions(n, rdf, u, s, v, type, tv)) return 1;
    n++;
  }
  return 0; 
}



PR_PUBLIC_API(void *)
RDF_GetSlotValue (RDF rdf, RDF_Resource u, RDF_Resource s, 
		    RDF_ValueType type, PRBool inversep, PRBool tv)
{
  int32 size =  rdf->numTranslators; 
  int32 n = 0;
  if ((s == gWebData->RDF_URL) && (tv) && (!inversep) && (type == RDF_STRING_TYPE))
    return  copyString(resourceID(u));         
  while (n < size) {
	RDFT translator = rdf->translators[n];
    void*  ans = callGetSlotValue(n, rdf, u, s, type, inversep, tv);
    if ((ans != NULL) && ((n == 0)||(!callHasAssertions(0, rdf, u, s, ans, type, !tv))))
    {

    	if (type == RDF_RESOURCE_TYPE) {
      		return addDep(rdf, ans) ;
      	} else {
		return ans;
	}
    }
    n++;
  }
  if (s == gCoreVocab->RDF_name && tv && !inversep && type == RDF_STRING_TYPE) 
    return makeResourceName(u);
  return NULL; 
}



RDFT
RDFTNamed (RDF rdf, char* name)
{
  int32 size =  rdf->numTranslators; 
  int32 n = 0;
  while (n < size) {
    char* nn = (*((RDFT*)rdf->translators + n))->url;
    if (RDF_STRCMP(nn, name) == 0) return  (*((RDFT*)rdf->translators + n));
    n = n + 1;
  }
  return NULL;
}



RDF_Cursor
getSlotValues (RDF rdf, RDF_Resource u, RDF_Resource s, 
			  RDF_ValueType type, PRBool inversep, PRBool tv)
{
  RDF_Cursor c = (RDF_Cursor) getMem(sizeof(struct RDF_CursorStruct));
  if (c == NULL)	return(NULL);
  c->u = u;
  c->s = s;
  c->queryType =  RDF_GET_SLOT_VALUES_QUERY;
  c->tv = tv;
  c->inversep = inversep;
  c->type = type;
  c->count = -1;
  c->rdf = rdf;
  c->current = NULL;
  while ((c->count < rdf->numTranslators-1) && (!c->current)) {
    c->count++;
    c->current = callGetSlotValues(c->count, rdf, u, s, type, inversep, tv);
  }
  if (!c->current) {
    freeMem(c);
    return NULL;
  } else {
    return c;
  }
}



PR_PUBLIC_API(RDF_Cursor)
RDF_GetTargets (RDF rdfDB, RDF_Resource source, RDF_Resource arcLabel, 
					  RDF_ValueType targetType,  PRBool tv)
{
	return getSlotValues(rdfDB, source, arcLabel, targetType, 0, tv);
}



PR_PUBLIC_API(RDF_Cursor)
RDF_GetSources (RDF rdfDB, RDF_Resource target, RDF_Resource arcLabel, 
					  RDF_ValueType sourceType,  PRBool tv)
{
  if (sourceType != RDF_RESOURCE_TYPE) return NULL;
  return getSlotValues(rdfDB, target, arcLabel, sourceType, 1, tv);
}



PR_PUBLIC_API(RDF_ValueType)
RDF_CursorValueType(RDF_Cursor c)
{
  if (c == NULL) {
    return 0;
  } else {
    return c->type;
  }
}



PR_PUBLIC_API(char*)
RDF_ValueDataSource(RDF_Cursor c)
{
  if (c == NULL) {
    return NULL;
  } else {
    RDF rdf = c->rdf;
    RDFT rdft;
    if (rdf == NULL) return NULL;
    rdft = rdf->translators[c->count];
    if (rdft) {
      return rdft->url;
    } else {
      return NULL;
    }
  }
}



PR_PUBLIC_API(RDF_Cursor)
RDF_ArcLabelsOut (RDF rdfDB, RDF_Resource u)
{
  RDF_Cursor c = (RDF_Cursor) getMem(sizeof(struct RDF_CursorStruct));
  if (c == NULL)   return(NULL);
  c->u = u;
  c->queryType =  RDF_ARC_LABELS_OUT_QUERY;
  c->type = RDF_RESOURCE_TYPE;
  c->count = -1;
  c->rdf = rdfDB;
  c->current = NULL;
  while ((c->count < rdfDB->numTranslators-1) && (!c->current)) {
    c->count++;
    c->current = callArcLabelsOut(c->count, rdfDB, u);
  } 
  if (!c->current) {
    freeMem(c);
    return NULL;
  } else {
    return c;
  }
}



PR_PUBLIC_API(RDF_Cursor)
RDF_ArcLabelsIn (RDF rdfDB, RDF_Resource u)
{
  RDF_Cursor c = (RDF_Cursor) getMem(sizeof(struct RDF_CursorStruct));
  if (c == NULL)   return(NULL);
  c->u = u;
  c->queryType =  RDF_ARC_LABELS_IN_QUERY;
  c->type = RDF_RESOURCE_TYPE;
  c->count = -1;
  c->rdf = rdfDB;
  c->current = NULL;
  while ((c->count < rdfDB->numTranslators-1) && (!c->current)) {
    c->count++;
    c->current = callArcLabelsIn(c->count, rdfDB, u);
  }
  if (!c->current) {
    freeMem(c);
    return NULL;
  } else {
    return c;
  }
}



PR_PUBLIC_API(void *)
RDF_NextValue (RDF_Cursor c)
{
  void* ans;
  RDF rdf;  
  if (c == NULL) return NULL;
  if (c->queryType == RDF_FIND_QUERY) return nextFindValue(c);
  rdf =  c->rdf;
  ans = (*rdf->translators[c->count]->nextValue)(rdf->translators[c->count], c->current);
  while (ans != NULL) {
    if ((c->count == 0) || (c->queryType != RDF_GET_SLOT_VALUES_QUERY)) {
      if (c->type == RDF_RESOURCE_TYPE)  return addDep(c->rdf, ans);
      else				 return ans;
      }
    if (((!c->inversep) && 
         (!(callHasAssertions(0, rdf, c->u, c->s, ans, c->type, !c->tv)))) ||
        ((c->inversep) && 
         (!(callHasAssertions(0, rdf, (RDF_Resource)ans, c->s, c->u, 
                              c->type, !c->tv))))) {
      if (c->type == RDF_RESOURCE_TYPE) {
        return addDep(c->rdf, (RDF_Resource)ans);
      } else return ans;
    }
    ans = (*rdf->translators[c->count]->nextValue)(rdf->translators[c->count], c->current);
  }
  (*rdf->translators[c->count]->disposeCursor)(rdf->translators[c->count], c->current);
  c->current = NULL;
  while ((c->count < rdf->numTranslators - 1) && (!c->current)) {
    c->count++;
    switch (c->queryType) {
    case RDF_GET_SLOT_VALUES_QUERY : 
      c->current = callGetSlotValues(c->count,rdf, c->u, c->s, c->type, 
                                     c->inversep, c->tv);
      break;
    case RDF_ARC_LABELS_OUT_QUERY :
      c->current = callArcLabelsOut(c->count, rdf, c->u);
      break;
    case RDF_ARC_LABELS_IN_QUERY :
      c->current = callArcLabelsIn(c->count, rdf, c->u);
      break;
    }
    
  }
  if (c->current == NULL) return NULL;
  return RDF_NextValue(c);
}



void 
disposeResourceInt (RDF rdf, RDF_Resource u)
{
  int32 size = rdf->numTranslators;
  int32 n = 0;
  PRBool ok = 1;
  while (n < size && ok) {
    ok = (callDisposeResource(n, rdf, u));
    n++;
  } 
  if (ok && (!lockedp(u))) possiblyGCResource(u);
}
    
  

PR_PUBLIC_API(RDF_Error) 
RDF_ReleaseResource (RDF db, RDF_Resource u)
{
  if (db && u) {    
	
    u->rdf = deleteFromRDFList(u->rdf, db);
    if (u->rdf == NULL)  disposeResourceInt(db, u);
  }
  return 0;
}



PR_PUBLIC_API(RDF_Error)
RDF_DisposeCursor (RDF_Cursor c)
{
  if ((c != NULL) && (c->queryType == RDF_FIND_QUERY) && (c->pdata)) freeMem(c->pdata);
  if (c != NULL) freeMem(c);
  return 0;
}



PRIntn
findEnumerator (PLHashEntry *he, PRIntn i, void *arg)
{
  RDF_Cursor c = (RDF_Cursor) arg;
  RDF_Resource u = (RDF_Resource)he->value;
  if (itemMatchesFind(c->rdf, u, c->s, c->value, c->match, c->type)) {
    /* if (c->size <= c->count) */ {
      RDF_Resource* newBlock = getMem(c->size + sizeof(RDF_Resource *));
      if (newBlock == NULL) return  HT_ENUMERATE_STOP;
      if (c->size > 0) {
	memcpy(newBlock, c->pdata, c->size);
	freeMem(c->pdata);
      }
      c->pdata = newBlock;
      c->size += sizeof(RDF_Resource *);
    }
    *((RDF_Resource*)(((char *)c->pdata) + (c->count*sizeof(RDF_Resource *)))) = u;
    c->count++;
  }
  return  HT_ENUMERATE_NEXT;
}



PR_PUBLIC_API(RDF_Cursor)
RDF_Find (RDF_Resource s, RDF_Resource match, void* v, RDF_ValueType type)
{
  RDF_Cursor c = (RDF_Cursor) getMem(sizeof(struct RDF_CursorStruct));
  c->s = s;
  c->match = match;
  c->value = v;
  c->type = type;
  c->queryType =  RDF_FIND_QUERY;
  c->rdf = gAllDBs->rdf;
  PL_HashTableEnumerateEntries(resourceHash, findEnumerator, c);
  c->count = 0;
  return c;
}



PRBool
matchStrings(RDF_Resource match, char *data, char *pattern)
{
	PRBool		ok = 0;

	if (match == NULL)	match = gCoreVocab->RDF_substring;

	if (match == gCoreVocab->RDF_substring)
	{
		ok = substring(pattern, data);
	}
	else if (match == gCoreVocab->RDF_stringEquals)
	{
		ok = (PRBool)(strcmp(data, pattern) == 0);
	}
	else if (match == gCoreVocab->RDF_stringNotEquals)
	{
		ok = (PRBool)compareStrings(data, pattern);
	}
	else if (match == gCoreVocab->RDF_stringStartsWith)
	{
		ok = startsWith(pattern, data);
	}
	else if (match == gCoreVocab->RDF_stringEndsWith)
	{
		ok = endsWith(pattern, data);
	}
	return(ok);
}



PRBool
itemMatchesFind (RDF r, RDF_Resource u, RDF_Resource s, void* v,
				RDF_Resource match, RDF_ValueType type)
{
	RDF_Cursor	c;
	void		*val;
    PRBool		ok = 0;

  if ((s == gWebData->RDF_URL) && (type == RDF_STRING_TYPE))
  {
	val = resourceID(u);
	ok = matchStrings(match, val, v);
	return(ok);
  }

  c = RDF_GetTargets(r, u, s, type, 1);
  if (c != NULL) {
    while ((val = RDF_NextValue(c)) != NULL) {
      if (type == RDF_RESOURCE_TYPE) {
        ok = (u == v);
      } else if (type == RDF_STRING_TYPE) {
      	if (s == gWebData->RDF_URL)
      	{
      		val = resourceID(u);
      	}
      	ok = matchStrings(match, val, v);
      }
      if (ok) {
        RDF_DisposeCursor(c);
        return 1;
      }
    }
    RDF_DisposeCursor(c);
  }
  return 0;
}



RDF_Resource
nextFindValue (RDF_Cursor c)
{
	RDF_Resource ans = NULL;

  if (((c->count*sizeof(RDF_Resource *)) < c->size) &&
  	(*(RDF_Resource *)((char *)c->pdata + (c->count*sizeof(RDF_Resource *))) != NULL))
  {
    ans = *(RDF_Resource *)((char *)c->pdata + (c->count*sizeof(RDF_Resource *)));
    c->count++;
  }
  return ans;
}



void
possiblyGCResource (RDF_Resource u)
{
  if ((nullp(u->pdata)) && (nullp(u->rarg1)) && (nullp(u->rarg2))) {
    PL_HashTableRemove(resourceHash,  resourceID(u));
    freeMem(u->url);
    freeMem(u);  
  }
}



PR_PUBLIC_API(RDF_Notification)
RDF_AddNotifiable (RDF rdfDB, RDF_NotificationProc callBack, 
					     RDF_Event ev, void* pdata)
{
  RDF_Notification ns = (RDF_Notification)getMem(sizeof(struct RDF_NotificationStruct));
  RDF_Event        nev = (RDF_Event)getMem(sizeof(struct RDF_EventStruct));
  memcpy((char*)nev, ev, sizeof(struct RDF_EventStruct));
  ns->theEvent = nev;
  ns->notifFunction = callBack;
  ns->pdata = pdata;
  ns->next = rdfDB->notifs;
  rdfDB->notifs = ns;
  ns->rdf = rdfDB;
  return ns;
}



PR_PUBLIC_API(RDF_Error)
RDF_DeleteNotifiable (RDF_Notification ns)
{
  RDF rdf = ns->rdf;
  RDF_Notification pr = rdf->notifs;
  RDF_Notification not;
  if (pr == ns) {
    rdf->notifs = pr->next; 
  } else {
    for (not = rdf->notifs; (not != NULL) ; not = not->next) {
      if (ns == not) 
	  {
		pr->next = not->next; 
		break;
      }
      pr = not;
    }
  }
  freeMem(ns->theEvent);
  freeMem(ns);
  return 0;
}



void
assertNotify (RDF rdf, RDF_Notification not, RDF_Resource u, RDF_Resource s, void* v, RDF_ValueType type, PRBool tv, char* ds)
 {
  RDF_Event nev = not->theEvent;
  RDF_AssertEvent ev = &(not->theEvent->event.assert);
  PRBool opTypeFixed = not->theEvent->eventType;
  PRBool uFixed      = (ev->u != NULL);
  PRBool sFixed		= (ev->s != NULL);
  PRBool vFixed		= (ev->v != NULL);
  RDF_EventType  oldEventType   = not->theEvent->eventType;
  if ((!opTypeFixed ||(not->theEvent->eventType & RDF_ASSERT_NOTIFY)) &&
      (!uFixed	  || (ev->u      == u))      &&
      (!vFixed    || (ev->v      == v))       &&
      (!sFixed      || (ev->s      == s))) {
    /*   if 	(callHasAssertions(0, rdf, u, s, v, type, !tv)) return; */
    nev->eventType = RDF_ASSERT_NOTIFY;
    ev->u      = u;
    ev->s      = s;
    ev->v      = v;
    ev->type   = type;
    ev->tv     = tv;
    ev->dataSource = ds;
    (*(not->notifFunction))(nev, not->pdata);
       nev->eventType = oldEventType;
    if (!uFixed)      ev->u = NULL;
    if (!sFixed)      ev->s = NULL;
    if (!vFixed)      ev->v = NULL;
    }
}



void
insertNotify (RDF rdf, RDF_Notification not, RDF_Resource u, RDF_Resource s, void* v, RDF_ValueType type, PRBool tv, char* ds)
{
  RDF_Event nev = not->theEvent;
  RDF_AssertEvent ev = &(not->theEvent->event.assert);
  PRBool opTypeFixed = not->theEvent->eventType;
  PRBool uFixed      = (ev->u != NULL);
  PRBool sFixed		= (ev->s != NULL);
  PRBool vFixed		= (ev->v != NULL);
  RDF_EventType  oldEventType   = not->theEvent->eventType;
  if ((!opTypeFixed ||(not->theEvent->eventType & RDF_INSERT_NOTIFY)) &&
      (!uFixed	  || (ev->u      == u))      &&
      (!vFixed    || (ev->v      == v))       &&
      (!sFixed      || (ev->s      == s))) {
    /*   if 	(callHasAssertions(0, rdf, u, s, v, type, !tv)) return; */
    nev->eventType = RDF_INSERT_NOTIFY;
    ev->u      = u;
    ev->s      = s;
    ev->v      = v;
    ev->type   = type;
    ev->tv     = tv;
    ev->dataSource = ds;
    (*(not->notifFunction))(nev, not->pdata);
        nev->eventType = oldEventType;
    if (!uFixed)      ev->u = NULL;
    if (!sFixed)      ev->s = NULL;
    if (!vFixed)      ev->v = NULL;
    }
}



void
unassertNotify (RDF_Notification not, RDF_Resource u, RDF_Resource s, void* v, RDF_ValueType type, char* ds)
{
  RDF_Event nev = not->theEvent;
  RDF_UnassertEvent ev = &(not->theEvent->event.unassert);
  PRBool opTypeFixed = not->theEvent->eventType;
  PRBool uFixed      = (ev->u != NULL);
  PRBool sFixed		= (ev->s != NULL);
  PRBool vFixed		= (ev->v != NULL);
  RDF_EventType  oldEventType   = not->theEvent->eventType;
  if ((!opTypeFixed ||(not->theEvent->eventType & RDF_DELETE_NOTIFY)) &&
      (!uFixed	  || (ev->u      == u))      &&
      (!vFixed    || (ev->v      == v))       &&
      (!sFixed      || (ev->s      == s))) {
    nev->eventType = RDF_DELETE_NOTIFY;
    ev->u      = u;
    ev->s      = s;
    ev->v      = v;
    ev->type   = type;
    ev->dataSource = ds;
    (*(not->notifFunction))(nev, not->pdata);
    nev->eventType = oldEventType;
    if (!uFixed)      ev->u = NULL;
    if (!sFixed)      ev->s = NULL;
    if (!vFixed)      ev->v = NULL;
    }
}



void
sendNotifications2 (RDFT r, RDF_EventType opType, RDF_Resource u, RDF_Resource s, void* v, RDF_ValueType type, PRBool tv)
{
  RDFL rl = r->rdf;

  if ((opType == RDF_ASSERT_NOTIFY) && gLocalStore &&
      (remoteStoreHasAssertion(gLocalStore, u, s, v, type, !tv))) {
	  return;
  }

  while (rl) {
    sendNotifications(rl->rdf, opType, u, s, v, type, tv, r->url);
    rl = rl->next;
  }
}



void
sendNotifications (RDF rdf, RDF_EventType opType, RDF_Resource u, RDF_Resource s, void* v, RDF_ValueType type, PRBool tv, char* ds)
{
  RDF_Notification not;
  if (rdf == NULL) return;
  for (not = rdf->notifs; (not != NULL) ; not = not->next) {
    if (!not->theEvent || (not->theEvent->eventType & opType)) {
		RDF_EventType otype = not->theEvent->eventType;
      switch (opType) 
	{
	case RDF_ASSERT_NOTIFY :
	  assertNotify(rdf, not, u, s, v, type, tv, ds);
	  break;
	case RDF_INSERT_NOTIFY :
	  insertNotify(rdf, not, u, s, v, type, tv, ds);
	  break;
	case RDF_DELETE_NOTIFY :
	  unassertNotify(not, u, s, v, type, ds);
	  break;
	case RDF_KILL_NOTIFY :
	  break;
	  /*	  killNotify(not, u); */
	}
	  not->theEvent->eventType = otype;
    }
  }
}
