/*------------------------------------------------------------------------------ | |
* Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team | |
* | |
* Distributable under the terms of either the Apache License (Version 2.0) or | |
* the GNU Lesser General Public License, as specified in the COPYING file. | |
------------------------------------------------------------------------------*/ | |
#include "CLucene/StdHeader.h" | |
#include "FieldCacheImpl.h" | |
CL_NS_USE(util) | |
CL_NS_USE(index) | |
CL_NS_DEF(search) | |
FieldCacheImpl::FieldCacheImpl(): | |
cache(false,true){ | |
} | |
FieldCacheImpl::~FieldCacheImpl(){ | |
cache.clear(); | |
} | |
FieldCacheImpl::FileEntry::FileEntry (const TCHAR* field, int32_t type) { | |
this->field = CLStringIntern::intern(field CL_FILELINE); | |
this->type = type; | |
this->custom = NULL; | |
this->_hashCode = 0; | |
} | |
/** Creates one of these objects for a custom comparator. */ | |
FieldCacheImpl::FileEntry::FileEntry (const TCHAR* field, SortComparatorSource* custom) { | |
this->field = CLStringIntern::intern(field CL_FILELINE); | |
this->type = SortField::CUSTOM; | |
this->custom = custom; | |
this->_hashCode = 0; | |
} | |
FieldCacheImpl::FileEntry::~FileEntry(){ | |
CLStringIntern::unintern(field); | |
} | |
size_t FieldCacheImpl::FileEntry::hashCode(){ | |
if ( _hashCode == 0 ){ | |
//todo: cache hashcode? | |
size_t ret = Misc::thashCode(field); | |
if ( custom != NULL ) | |
ret = ret ^ custom->hashCode(); | |
ret = ret ^ (type*7); //type with a seed | |
_hashCode = ret; | |
} | |
return _hashCode; | |
} | |
int32_t FieldCacheImpl::FileEntry::compareTo(const FieldCacheImpl::FileEntry* other) const{ | |
if ( other->field == this->field ){ | |
if ( other->type == this->type ){ | |
if ( other->custom == NULL ){ | |
if ( this->custom == NULL ) | |
return 0; //both null | |
else | |
return 1; | |
}else if ( this->custom == NULL ) | |
return -1; | |
else if ( other->custom < this->custom ) | |
return -1; | |
else if ( other->custom > this->custom ) | |
return 1; | |
else | |
return 0; | |
}else if ( other->type > this->type ) | |
return 1; | |
else | |
return -1; | |
}else | |
return _tcscmp(other->field,this->field); | |
} | |
/** Two of these are equal iff they reference the same field and type. */ | |
/*bool FieldCacheImpl::FileEntry::equals (FileEntry* other) { | |
if (other->field == field && other->type == type) { | |
if (other->custom == NULL) { | |
if (custom == NULL) | |
return true; | |
} else if (other->custom->equals (custom)) { | |
return true; | |
} | |
} | |
}*/ | |
/** Composes a hashcode based on the field and type. */ | |
/*size_t FieldCacheImpl::FileEntry::hashCode() { | |
return field->hashCode() ^ type ^ (custom==NULL ? 0 : custom->hashCode()); | |
}*/ | |
/** See if an object is in the cache. */ | |
FieldCacheAuto* FieldCacheImpl::lookup (IndexReader* reader, const TCHAR* field, int32_t type) { | |
FieldCacheAuto* ret = NULL; | |
FileEntry* entry = _CLNEW FileEntry (field, type); | |
{ | |
SCOPED_LOCK_MUTEX(THIS_LOCK) | |
fieldcacheCacheReaderType* readerCache = cache.get(reader); | |
if (readerCache != NULL) | |
ret = readerCache->get (entry); | |
_CLDELETE(entry); | |
} | |
return ret; | |
} | |
/** See if a custom object is in the cache. */ | |
FieldCacheAuto* FieldCacheImpl::lookup (IndexReader* reader, const TCHAR* field, SortComparatorSource* comparer) { | |
FieldCacheAuto* ret = NULL; | |
FileEntry* entry = _CLNEW FileEntry (field, comparer); | |
{ | |
SCOPED_LOCK_MUTEX(THIS_LOCK) | |
fieldcacheCacheReaderType* readerCache = cache.get(reader); | |
if (readerCache != NULL) | |
ret = readerCache->get (entry); | |
_CLDELETE(entry); | |
} | |
return ret; | |
} | |
void FieldCacheImpl::closeCallback(CL_NS(index)::IndexReader* reader, void* fieldCacheImpl){ | |
FieldCacheImpl* fci = (FieldCacheImpl*)fieldCacheImpl; | |
SCOPED_LOCK_MUTEX(fci->THIS_LOCK) | |
fci->cache.remove(reader); | |
} | |
/** Put an object into the cache. */ | |
void FieldCacheImpl::store (IndexReader* reader, const TCHAR* field, int32_t type, FieldCacheAuto* value) { | |
FileEntry* entry = _CLNEW FileEntry (field, type); | |
{ | |
SCOPED_LOCK_MUTEX(THIS_LOCK) | |
fieldcacheCacheReaderType* readerCache = cache.get(reader); | |
if (readerCache == NULL) { | |
readerCache = _CLNEW fieldcacheCacheReaderType; | |
cache.put(reader,readerCache); | |
reader->addCloseCallback(closeCallback, this); | |
} | |
readerCache->put (entry, value); | |
//this is supposed to return the previous value, but it needs to be deleted!!! | |
} | |
} | |
/** Put a custom object into the cache. */ | |
void FieldCacheImpl::store (IndexReader* reader, const TCHAR* field, SortComparatorSource* comparer, FieldCacheAuto* value) { | |
FileEntry* entry = _CLNEW FileEntry (field, comparer); | |
{ | |
SCOPED_LOCK_MUTEX(THIS_LOCK) | |
fieldcacheCacheReaderType* readerCache = cache.get(reader); | |
if (readerCache == NULL) { | |
readerCache = _CLNEW fieldcacheCacheReaderType; | |
cache.put(reader, readerCache); | |
reader->addCloseCallback(FieldCacheImpl::closeCallback, this); | |
} | |
readerCache->put(entry, value); | |
//this is supposed to return the previous value, but it needs to be deleted!!! | |
} | |
} | |
// inherit javadocs | |
FieldCacheAuto* FieldCacheImpl::getInts (IndexReader* reader, const TCHAR* field) { | |
field = CLStringIntern::intern(field CL_FILELINE); | |
FieldCacheAuto* ret = lookup (reader, field, SortField::INT); | |
if (ret == NULL) { | |
int32_t retLen = reader->maxDoc(); | |
int32_t* retArray = _CL_NEWARRAY(int32_t,retLen); | |
memset(retArray,0,sizeof(int32_t)*retLen); | |
if (retLen > 0) { | |
TermDocs* termDocs = reader->termDocs(); | |
Term* term = _CLNEW Term (field, LUCENE_BLANK_STRING, false); | |
TermEnum* termEnum = reader->terms (term); | |
_CLDECDELETE(term); | |
try { | |
if (termEnum->term(false) == NULL) { | |
_CLTHROWA(CL_ERR_Runtime,"no terms in field"); //todo: add detailed error: + field); | |
} | |
do { | |
Term* term = termEnum->term(false); | |
if (term->field() != field) | |
break; | |
TCHAR* end; | |
int32_t termval = (int32_t)_tcstoi64(term->text(), &end, 10); | |
termDocs->seek (termEnum); | |
while (termDocs->next()) { | |
retArray[termDocs->doc()] = termval; | |
} | |
} while (termEnum->next()); | |
} _CLFINALLY( | |
termDocs->close(); | |
_CLDELETE(termDocs); | |
termEnum->close(); | |
_CLDELETE(termEnum); | |
) | |
} | |
FieldCacheAuto* fa = _CLNEW FieldCacheAuto(retLen,FieldCacheAuto::INT_ARRAY); | |
fa->intArray = retArray; | |
store (reader, field, SortField::INT, fa); | |
CLStringIntern::unintern(field); | |
return fa; | |
} | |
CLStringIntern::unintern(field); | |
return ret; | |
} | |
// inherit javadocs | |
FieldCacheAuto* FieldCacheImpl::getFloats (IndexReader* reader, const TCHAR* field){ | |
field = CLStringIntern::intern(field CL_FILELINE); | |
FieldCacheAuto* ret = lookup (reader, field, SortField::FLOAT); | |
if (ret == NULL) { | |
int32_t retLen = reader->maxDoc(); | |
qreal* retArray = _CL_NEWARRAY(qreal,retLen); | |
memset(retArray,0,sizeof(qreal)*retLen); | |
if (retLen > 0) { | |
TermDocs* termDocs = reader->termDocs(); | |
Term* term = _CLNEW Term (field, LUCENE_BLANK_STRING, false); | |
TermEnum* termEnum = reader->terms (term); | |
_CLDECDELETE(term); | |
try { | |
if (termEnum->term(false) == NULL) { | |
_CLTHROWA(CL_ERR_Runtime,"no terms in field "); //todo: make richer error + field); | |
} | |
do { | |
Term* term = termEnum->term(false); | |
if (term->field() != field) | |
break; | |
TCHAR* tmp; | |
qreal termval = _tcstod(term->text(),&tmp); | |
termDocs->seek (termEnum); | |
while (termDocs->next()) { | |
retArray[termDocs->doc()] = termval; | |
} | |
} while (termEnum->next()); | |
} _CLFINALLY( | |
termDocs->close(); | |
_CLDELETE(termDocs); | |
termEnum->close(); | |
_CLDELETE(termEnum); | |
) | |
} | |
FieldCacheAuto* fa = _CLNEW FieldCacheAuto(retLen,FieldCacheAuto::FLOAT_ARRAY); | |
fa->floatArray = retArray; | |
store (reader, field, SortField::FLOAT, fa); | |
CLStringIntern::unintern(field); | |
return fa; | |
} | |
CLStringIntern::unintern(field); | |
return ret; | |
} | |
// inherit javadocs | |
FieldCacheAuto* FieldCacheImpl::getStrings (IndexReader* reader, const TCHAR* field){ | |
//todo: this is not really used, i think? | |
field = CLStringIntern::intern(field CL_FILELINE); | |
FieldCacheAuto* ret = lookup (reader, field, SortField::STRING); | |
if (ret == NULL) { | |
int32_t retLen = reader->maxDoc(); | |
TCHAR** retArray = _CL_NEWARRAY(TCHAR*,retLen+1); | |
memset(retArray,0,sizeof(TCHAR*)*(retLen+1)); | |
if (retLen > 0) { | |
TermDocs* termDocs = reader->termDocs(); | |
Term* term = _CLNEW Term (field, LUCENE_BLANK_STRING, false); | |
TermEnum* termEnum = reader->terms (term); | |
_CLDECDELETE(term); | |
try { | |
if (termEnum->term(false) == NULL) { | |
_CLTHROWA(CL_ERR_Runtime,"no terms in field "); //todo: extend to + field); | |
} | |
do { | |
Term* term = termEnum->term(false); | |
if (term->field() != field) | |
break; | |
const TCHAR* termval = term->text(); | |
termDocs->seek (termEnum); | |
while (termDocs->next()) { | |
retArray[termDocs->doc()] = STRDUP_TtoT(termval); //todo: any better way of doing this??? | |
} | |
} while (termEnum->next()); | |
} _CLFINALLY( | |
retArray[retLen]=NULL; | |
termDocs->close(); | |
_CLDELETE(termDocs); | |
termEnum->close(); | |
_CLDELETE(termEnum); | |
) | |
} | |
FieldCacheAuto* fa = _CLNEW FieldCacheAuto(retLen,FieldCacheAuto::STRING_ARRAY); | |
fa->stringArray = retArray; | |
fa->ownContents=true; | |
store (reader, field, SortField::STRING, fa); | |
CLStringIntern::unintern(field); | |
return fa; | |
} | |
CLStringIntern::unintern(field); | |
return ret; | |
} | |
// inherit javadocs | |
FieldCacheAuto* FieldCacheImpl::getStringIndex (IndexReader* reader, const TCHAR* field){ | |
field = CLStringIntern::intern(field CL_FILELINE); | |
FieldCacheAuto* ret = lookup (reader, field, STRING_INDEX); | |
int32_t t = 0; // current term number | |
if (ret == NULL) { | |
int32_t retLen = reader->maxDoc(); | |
int32_t* retArray = _CL_NEWARRAY(int32_t,retLen); | |
memset(retArray,0,sizeof(int32_t)*retLen); | |
TCHAR** mterms = _CL_NEWARRAY(TCHAR*,retLen+2); | |
mterms[0]=NULL; | |
if ( retLen > 0 ) { | |
TermDocs* termDocs = reader->termDocs(); | |
Term* term = _CLNEW Term (field, LUCENE_BLANK_STRING, false); | |
TermEnum* termEnum = reader->terms (term); | |
_CLDECDELETE(term); | |
CND_PRECONDITION(t+1 <= retLen, "t out of bounds"); | |
// an entry for documents that have no terms in this field | |
// should a document with no terms be at top or bottom? | |
// this puts them at the top - if it is changed, FieldDocSortedHitQueue | |
// needs to change as well. | |
mterms[t++] = NULL; | |
try { | |
if (termEnum->term(false) == NULL) { | |
_CLTHROWA(CL_ERR_Runtime,"no terms in field"); //todo: make rich message " + field); | |
} | |
do { | |
Term* term = termEnum->term(false); | |
if (term->field() != field) | |
break; | |
// store term text | |
// we expect that there is at most one term per document | |
if (t >= retLen+1) | |
_CLTHROWA(CL_ERR_Runtime,"there are more terms than documents in field"); //todo: rich error \"" + field + "\""); | |
mterms[t] = STRDUP_TtoT(term->text()); | |
termDocs->seek (termEnum); | |
while (termDocs->next()) { | |
retArray[termDocs->doc()] = t; | |
} | |
t++; | |
} while (termEnum->next()); | |
CND_PRECONDITION(t<retLen+2,"t out of bounds"); | |
mterms[t] = NULL; | |
} _CLFINALLY( | |
termDocs->close(); | |
_CLDELETE(termDocs); | |
termEnum->close(); | |
_CLDELETE(termEnum); | |
); | |
if (t == 0) { | |
// if there are no terms, make the term array | |
// have a single NULL entry | |
_CLDELETE_ARRAY(mterms); | |
mterms = _CL_NEWARRAY(TCHAR*,1); //todo: delete old mterms? | |
mterms[0]=NULL; | |
} else if (t < retLen) { //todo: check, was mterms.length | |
// if there are less terms than documents, | |
// trim off the dead array space | |
//const TCHAR** terms = _CL_NEWARRAY(TCHAR,t); | |
//System.arraycopy (mterms, 0, terms, 0, t); | |
//mterms = terms; | |
//we simply shorten the length of the array... | |
} | |
} | |
FieldCache::StringIndex* value = _CLNEW FieldCache::StringIndex (retArray, mterms,t); | |
FieldCacheAuto* fa = _CLNEW FieldCacheAuto(retLen,FieldCacheAuto::STRING_INDEX); | |
fa->stringIndex = value; | |
fa->ownContents=true; | |
store (reader, field, STRING_INDEX, fa); | |
CLStringIntern::unintern(field); | |
return fa; | |
} | |
CLStringIntern::unintern(field); | |
return ret; | |
} | |
// inherit javadocs | |
FieldCacheAuto* FieldCacheImpl::getAuto (IndexReader* reader, const TCHAR* field) { | |
field = CLStringIntern::intern(field CL_FILELINE); | |
FieldCacheAuto* ret = lookup (reader, field, SortField::AUTO); | |
if (ret == NULL) { | |
Term* term = _CLNEW Term (field, LUCENE_BLANK_STRING, false); | |
TermEnum* enumerator = reader->terms (term); | |
_CLDECDELETE(term); | |
try { | |
Term* term = enumerator->term(false); | |
if (term == NULL) { | |
_CLTHROWA(CL_ERR_Runtime,"no terms in field - cannot determine sort type"); //todo: make rich error: " + field + " | |
} | |
if (term->field() == field) { | |
const TCHAR* termtext = term->text(); | |
size_t termTextLen = term->textLength(); | |
bool isint=true; | |
for ( size_t i=0;i<termTextLen;i++ ){ | |
if ( _tcschr(_T("0123456789 +-"),termtext[i]) == NULL ){ | |
isint = false; | |
break; | |
} | |
} | |
if ( isint ) | |
ret = getInts (reader, field); | |
else{ | |
bool isfloat=true; | |
int32_t searchLen = termTextLen; | |
if ( termtext[termTextLen-1] == 'f' ) | |
searchLen--; | |
for ( int32_t i=0;i<searchLen;i++ ){ | |
if ( _tcschr(_T("0123456789 Ee.+-"),termtext[i]) == NULL ){ | |
isfloat = false; | |
break; | |
} | |
} | |
if ( isfloat ) | |
ret = getFloats (reader, field); | |
else{ | |
ret = getStringIndex (reader, field); | |
} | |
} | |
if (ret != NULL) { | |
store (reader, field, SortField::AUTO, ret); | |
} | |
} else { | |
_CLTHROWA (CL_ERR_Runtime,"field does not appear to be indexed"); //todo: make rich error: \"" + field + "\" | |
} | |
} _CLFINALLY( enumerator->close(); _CLDELETE(enumerator) ); | |
} | |
CLStringIntern::unintern(field); | |
return ret; | |
} | |
// inherit javadocs | |
FieldCacheAuto* FieldCacheImpl::getCustom (IndexReader* reader, const TCHAR* field, SortComparator* comparator){ | |
field = CLStringIntern::intern(field CL_FILELINE); | |
FieldCacheAuto* ret = lookup (reader, field, comparator); | |
if (ret == NULL) { | |
int32_t retLen = reader->maxDoc(); | |
Comparable** retArray = _CL_NEWARRAY(Comparable*,retLen); | |
memset(retArray,0,sizeof(Comparable*)*retLen); | |
if (retLen > 0) { | |
TermDocs* termDocs = reader->termDocs(); | |
TermEnum* termEnum = reader->terms (); | |
try { | |
if (termEnum->term(false) == NULL) { | |
_CLTHROWA(CL_ERR_Runtime,"no terms in field "); //todo: make rich error + field); | |
} | |
do { | |
Term* term = termEnum->term(false); | |
if (term->field() != field) | |
break; | |
Comparable* termval = comparator->getComparable (term->text()); | |
termDocs->seek (termEnum); | |
while (termDocs->next()) { | |
retArray[termDocs->doc()] = termval; | |
} | |
} while (termEnum->next()); | |
} _CLFINALLY ( | |
termDocs->close(); | |
_CLDELETE(termDocs); | |
termEnum->close(); | |
_CLDELETE(termEnum); | |
); | |
} | |
FieldCacheAuto* fa = _CLNEW FieldCacheAuto(retLen,FieldCacheAuto::COMPARABLE_ARRAY); | |
fa->comparableArray = retArray; | |
fa->ownContents=true; | |
store (reader, field, SortField::CUSTOM, fa); | |
CLStringIntern::unintern(field); | |
return fa; | |
} | |
CLStringIntern::unintern(field); | |
return ret; | |
} | |
FieldCacheImpl::fieldcacheCacheReaderType::fieldcacheCacheReaderType(){ | |
setDeleteKey(false); | |
setDeleteValue(false); | |
} | |
FieldCacheImpl::fieldcacheCacheReaderType::~fieldcacheCacheReaderType(){ | |
iterator itr = begin(); | |
while ( itr != end() ){ | |
FileEntry* f = itr->first; | |
if ( f->getType() != SortField::AUTO ) | |
_CLDELETE( itr->second ); | |
_CLDELETE( f ); | |
++itr; | |
} | |
clear(); | |
} | |
CL_NS_END |