Dictionary.cpp
上传用户:hmc_gdtv
上传日期:2013-08-04
资源大小:798k
文件大小:16k
源码类别:

Windows Mobile

开发平台:

Visual C++

  1. /*
  2.  * Copyright (c) 2001,2002,2003 Mike Matsnev.  All Rights Reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice immediately at the beginning of the file, without modification,
  10.  *    this list of conditions, and the following disclaimer.
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in the
  13.  *    documentation and/or other materials provided with the distribution.
  14.  * 3. Absolutely no warranty of function or purpose is made by the author
  15.  *    Mike Matsnev.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  * 
  28.  * $Id: Dictionary.cpp,v 1.23.2.3 2004/07/07 12:04:46 mike Exp $
  29.  * 
  30.  */
  31. #include <afx.h>
  32. #include <afxtempl.h>
  33. #include "ptr.h"
  34. #include "zlib.h"
  35. #include "Unicode.h"
  36. #include "RFile.h"
  37. #include "TextParser.h"
  38. #include "Dictionary.h"
  39. #ifdef _DEBUG
  40. #undef THIS_FILE
  41. static char THIS_FILE[]=__FILE__;
  42. #define new DEBUG_NEW
  43. #endif
  44. class Dict : public IDict
  45. {
  46. public:
  47.   Dict(RFile *fp);
  48.   ~Dict() { }
  49.   int   NumWords() { return m_numwords; }
  50.   Buffer<wchar_t> GetWordW(int index);
  51.   bool   Find(const wchar_t *word,int& index,int& found);
  52.   bool   FindImp(const wchar_t *word,int& index,int *found);
  53.   bool   Valid() { return m_ok; }
  54.   int   GetStartPofWord(int index);
  55.   int   GetWordFromP(int para);
  56.   int   GetNumP() { return m_numpara; }
  57. protected:
  58.   struct Block {
  59.     Buffer<char>  key;     /* key */
  60.     int wordidx;    /* first word index */
  61.     int numwords;   /* number of words in this block */
  62.     int size;     /* uncompressed block size */
  63.     int csize;     /* compressed size */
  64.     int off;     /* offset in the file */
  65.     int numpara;    /* number of paragraphs in this block */
  66.     int paraidx;    /* starting paragraph */
  67.   };
  68.   kilo::auto_ptr<RFile> m_rf;
  69.   DWORD m_numblk;
  70.   DWORD m_numwords;
  71.   int m_curblk;
  72.   CPtrArray m_windex; // words in current block
  73.   CPtrArray m_kindex; // keys
  74.   CUIntArray m_pindex; // paragraphs
  75.   Buffer<char> m_buffer;
  76.   CArray<Block,Block&> m_blocks;
  77.   bool m_ok;
  78.   LCID m_lcid;
  79.   UINT m_ms_codepage;
  80.   int m_codepage;
  81.   int m_numpara;
  82.   bool       GetBlk(int num);
  83.   const char  *GetWordImp(int index);
  84.   bool       OpenOld();
  85.   bool       OpenNew();
  86. };
  87. static DWORD    getdword(RFile *fp) {
  88.   BYTE   b[4];
  89.   int   rd=fp->read2(b,4);
  90.   if (rd!=4)
  91.     return 0;
  92.   return ((DWORD)b[3]<<24)|((DWORD)b[2]<<16)|((DWORD)b[1])<<8|b[0];
  93. }
  94. bool Dict::OpenNew() {
  95.   if ((m_lcid=getdword(m_rf.get()))==0)
  96.     return false;
  97.   if (!IsValidLocale(m_lcid,LCID_INSTALLED))
  98.     m_lcid=GetUserDefaultLCID();
  99.   if ((m_numwords=getdword(m_rf.get()))==0)
  100.     return false;
  101.   if ((m_ms_codepage=getdword(m_rf.get()))==0)
  102.     return false;
  103.   if ((m_codepage=Unicode::GetIntCodePage(m_ms_codepage))<0)
  104.     m_codepage=Unicode::DefaultCodePage();
  105.   DWORD btab=getdword(m_rf.get());
  106.   if (btab==0)
  107.     return false;
  108.   m_rf->seek(btab);
  109.   if ((m_numblk=getdword(m_rf.get()))==0 || m_numblk>m_numwords)
  110.     return false;
  111.   DWORD idx,i,off,maxblock,paraidx;
  112.   for (i=idx=paraidx=maxblock=0,off=20;i<(int)m_numblk;++i) {
  113.     Block   blk;
  114.     int     keylen;
  115.     if ((blk.size=getdword(m_rf.get()))==0)
  116.       return false;
  117.     if ((blk.csize=getdword(m_rf.get()))==0)
  118.       return false;
  119.     if ((keylen=getdword(m_rf.get()))==0)
  120.       return false;
  121.     if ((blk.numwords=getdword(m_rf.get()))==0)
  122.       return false;
  123.     if ((blk.numpara=getdword(m_rf.get()))==0)
  124.       return false;
  125.     blk.numpara+=blk.numwords; // append and empty line after each word
  126.     blk.wordidx=idx;
  127.     blk.paraidx=paraidx;
  128.     blk.off=off;
  129.     idx+=blk.numwords;
  130.     paraidx+=blk.numpara;
  131.     off+=blk.csize;
  132.     blk.key=Buffer<char>(keylen);
  133.     m_blocks.Add(blk);
  134.     if (blk.size>(int)maxblock)
  135.       maxblock=blk.size;
  136.   }
  137.   if (idx!=m_numwords)
  138.     return false;
  139.   m_numpara=paraidx;
  140.   for (i=0;i<(int)m_numblk;++i)
  141.     if ((m_rf->read2(m_blocks[i].key,m_blocks[i].key.size()))!=(DWORD)m_blocks[i].key.size())
  142.       return false;
  143.   m_buffer=Buffer<char>(maxblock);
  144.   return true;
  145. }
  146. Dict::Dict(RFile *fp) :
  147.   m_ok(false), m_numwords(0), m_numpara(0), m_numblk(0), m_curblk(-1), m_rf(fp)
  148. {
  149.   if (!OpenNew())
  150.       goto fail;
  151.   m_ok=true;
  152.   return;
  153. fail:
  154.   m_buffer=Buffer<char>();
  155.   m_blocks.RemoveAll();
  156. }
  157. bool  Dict::GetBlk(int num) {
  158.   if (num<0 || num>=(int)m_numblk)
  159.     return false;
  160.   if (m_curblk==num)
  161.     return true;
  162.   m_curblk=-1;
  163.   m_rf->seek(m_blocks[num].off);
  164.   if (m_blocks[num].size==m_blocks[num].csize) { // uncompressed
  165.     if (m_blocks[num].size!=(int)m_rf->read2(m_buffer,m_blocks[num].size))
  166.       return false;
  167.   } else {
  168.     Buffer<unsigned char>    in(m_blocks[num].csize);
  169.     if (m_blocks[num].csize!=(int)m_rf->read2(in,m_blocks[num].csize))
  170.       return false;
  171.     uLongf  len=m_blocks[num].size;
  172.     int ret=uncompress((unsigned char *)(char *)m_buffer,&len,
  173.       in,m_blocks[num].csize);
  174.     if (ret!=Z_OK || (int)len!=m_blocks[num].size)
  175.       return false;
  176.   }
  177.   char   *p=m_buffer;
  178.   char   *e=p+m_blocks[num].size;
  179.   int   i,pnum;
  180.   m_windex.SetSize(m_blocks[num].numwords);
  181.   m_kindex.SetSize(m_blocks[num].numwords);
  182.   m_pindex.SetSize(m_blocks[num].numwords+1);
  183.   for (i=pnum=0;i<m_blocks[num].numwords && p<e;++i) {
  184.     m_kindex[i]=p;
  185.     m_pindex[i]=pnum;
  186.     while (p<e && *p)
  187.       ++p;
  188.     if (p<e)
  189.       ++p;
  190.     m_windex[i]=p;
  191.     while (p<e && *p) {
  192.       if (*p=='n')
  193. ++pnum;
  194.       ++p;
  195.     }
  196.     if (p<e)
  197.       ++p;
  198.     pnum+=2; // implicit empty line after each word
  199.   }
  200.   if (i!=m_blocks[num].numwords || pnum!=m_blocks[num].numpara)
  201.     return false;
  202.   m_curblk=num;
  203.   m_pindex[m_blocks[num].numwords]=m_blocks[num].numpara;
  204.   return true;
  205. }
  206. Buffer<wchar_t> Dict::GetWordW(int index) {
  207.   const char  *word=GetWordImp(index);
  208.   if (!word)
  209.     return Buffer<wchar_t>();
  210.   return Unicode::ToWCbuf(m_codepage,word,strlen(word));
  211. }
  212. CString   IDict::GetWord(int index) {
  213.   CString     ret(Unicode::ToCS(GetWordW(index)));
  214.   ret.Replace(_T("n"),_T("rn"));
  215.   return ret;
  216. }
  217. const char *Dict::GetWordImp(int index) {
  218.   if (index<0 || index>=(int)m_numwords)
  219.     return NULL;
  220.   if (m_curblk<0 || index<m_blocks[m_curblk].wordidx ||
  221.       index>=m_blocks[m_curblk].wordidx+m_blocks[m_curblk].numwords)
  222.   {
  223.     int low=0;
  224.     int high=m_numblk-1;
  225.     int mid;
  226.     for (int ni=0;;++ni) {
  227.       if (ni>(int)m_numblk) // prevent loops on unsorted invalid data
  228. return NULL;
  229.       if (low>high)
  230. return NULL;
  231.       mid=(low+high)>>1;
  232.       if (index<m_blocks[mid].wordidx)
  233. high=mid-1;
  234.       else if (index>=m_blocks[mid].wordidx+m_blocks[mid].numwords)
  235. low=mid+1;
  236.       else
  237. break;
  238.     }
  239.     if (!GetBlk(mid))
  240.       return NULL;
  241.   }
  242.   return (const char *)m_windex[index-m_blocks[m_curblk].wordidx];
  243. }
  244. static int    compare_buf_str(Buffer<char>& b1,const char *b2,int l2=-1) {
  245.   if (l2<0)
  246.     l2=strlen(b2);
  247.   int res=memcmp(b1,b2,min(b1.size(),l2));
  248.   if (res==0)
  249.     res=b1.size()<l2 ? -1 : b1.size()>l2 ? 1 : 0;
  250.   return res;
  251. }
  252. static int    compare_buf_str_len(Buffer<char>& b1,const char *b2,int l2=-1) {
  253.   if (l2<0)
  254.     l2=strlen(b2);
  255.   if (l2>b1.size())
  256.     l2=b1.size();
  257.   const char *p=b1;
  258.   while (l2-->0 && *p++==*b2++) ;
  259.   return p-b1;
  260. }
  261. static inline int    compare_bufs(Buffer<char>& b1,Buffer<char>& b2) {
  262.   return compare_buf_str(b1,b2,b2.size());
  263. }
  264. bool Dict::FindImp(const wchar_t *word,int& index,int *found) {
  265.   Buffer<char>   sortkey(Unicode::SortKey(m_lcid,word));
  266.   int   low=0;
  267.   int   high=m_numblk-1;
  268.   int   mid;
  269.   for (int ni=0;;++ni) {
  270.     if (ni>(int)m_numblk) // prevent loops on unsorted data
  271.       return false;
  272.     if (low>high) {
  273.       if (low==0) {
  274. index=0;
  275. if (found) {
  276.   *found=0;
  277.   return true;
  278. }
  279.       }
  280.       return false;
  281.     }
  282.     mid=(low+high)>>1;
  283.     int cmp=compare_bufs(sortkey,m_blocks[mid].key);
  284.     if (cmp<0)
  285.       high=mid-1;
  286.     else {
  287.       if (mid==(int)m_numblk-1) // last block, stop search
  288. break;
  289.       cmp=compare_bufs(sortkey,m_blocks[mid+1].key);
  290.       if (cmp<0) // found it
  291. break;
  292.       low=mid+1;
  293.     }
  294.   }
  295.   int blk=mid;
  296.   if (!GetBlk(blk))
  297.     return false;
  298.   low=0;
  299.   high=m_blocks[blk].numwords-1;
  300.   for (int nj=0;;++nj) {
  301.     if (nj>m_blocks[blk].numwords) // prevent loops on invalid data
  302.       return false;
  303.     if (low>high) { // no such word in this block
  304.       if (!found) // shortcut
  305. return false;
  306.       int   idx=m_blocks[blk].wordidx+high;
  307.       if (idx<0)
  308. idx=0;
  309.       if (idx<(int)m_numwords-1) {
  310. if (!GetWordImp(idx))
  311.   return false;
  312. int cur=idx-m_blocks[m_curblk].wordidx;
  313. int l1=compare_buf_str_len(sortkey,(const char *)m_kindex[cur]);
  314. if (++cur>=m_blocks[m_curblk].numwords) {
  315.   if (!GetWordImp(idx+1))
  316.     return false;
  317.   cur=0;
  318. }
  319. if (compare_buf_str_len(sortkey,(const char *)m_kindex[cur])>l1)
  320.   ++idx;
  321.       }
  322.       index=idx;
  323.       *found=2;
  324.       return true;
  325.     }
  326.     mid=(low+high)>>1;
  327.     int cmp=compare_buf_str(sortkey,(const char *)m_kindex[mid]);
  328.     if (cmp<0)
  329.       high=mid-1;
  330.     else if (cmp>0)
  331.       low=mid+1;
  332.     else { // found
  333.       index=m_blocks[blk].wordidx+mid;
  334.       if (found)
  335. *found=1;
  336.       return true;
  337.     }
  338.   }
  339. }
  340. static struct {
  341.   const wchar_t   *pattern;
  342.   const wchar_t   *replacement;
  343. } endings[]={
  344.   { L"s", NULL  },
  345.   { L"se", NULL  },
  346.   { L"sei", L"y"  },
  347.   { L"de", NULL  },
  348.   { L"de", L"e"  },
  349.   { L"dei", L"y"  },
  350.   { L"de.", L"1"  },
  351.   { L"gni", NULL  },
  352.   { L"gni", L"e"  },
  353.   { L"gniy", L"ie" },
  354.   { L"gni.", L"1"  },
  355.   { L"re", NULL  },
  356.   { L"re", L"e"  },
  357.   { L"rei", L"y"  },
  358.   { L"re." L"1"  },
  359.   { L"tse", NULL  },
  360.   { L"tse", L"e"  },
  361.   { L"tsei", L"y"  },
  362.   { L"tse.", L"1"  },
  363.   { NULL, NULL  }
  364. };
  365. bool  Dict::Find(const wchar_t *word,int& index,int& found) {
  366.   if (PRIMARYLANGID(m_lcid)!=LANG_ENGLISH)
  367.     return FindImp(word,index,&found);
  368.   // handle english word endings
  369.   wchar_t tmpbuf[64];
  370.   int wordlen=wcslen(word);
  371.   if (wordlen>=sizeof(tmpbuf)/sizeof(tmpbuf[0]))
  372.     return FindImp(word,index,&found);
  373.   // check exact match
  374.   if (FindImp(word,index,NULL)) {
  375.     found=1;
  376.     return true;
  377.   }
  378.   for (int ending=0;endings[ending].pattern;++ending) {
  379.     const wchar_t   *pattern=endings[ending].pattern;
  380.     for (int i=0;i<wordlen;++i) {
  381.       if (pattern[i]==L'.') { // doubled char here
  382. if (i+1<wordlen && word[wordlen-i-1]==word[wordlen-i-2]) {
  383.   i+=2;
  384.   goto match;
  385. }
  386. break;
  387.       }
  388.       if (!pattern[i]) { // end of pattern, matched
  389. match:
  390. int   k;
  391. for (k=0;k<wordlen-i;++k)
  392.   tmpbuf[k]=word[k];
  393. const wchar_t *replacement=endings[ending].replacement;
  394. if (replacement) {
  395.   if (replacement[0]==L'1')
  396.     tmpbuf[k++]=word[wordlen-i];
  397.   else
  398.     while (*replacement)
  399.       tmpbuf[k++]=*replacement++;
  400. }
  401. tmpbuf[k++]=L'';
  402. // relookup
  403. if (FindImp(tmpbuf,index,NULL)) {
  404.   found=1;
  405.   return true;
  406. }
  407. break;
  408.       }
  409.       if (pattern[i]!=word[wordlen-i-1])
  410. break;
  411.     }
  412.   }
  413.   // still no match, proceed as usual
  414.   return FindImp(word,index,&found);
  415. }
  416. int   Dict::GetWordFromP(int para) {
  417.   if (para<0 || para>=m_numpara)
  418.     return 0;
  419.   int low=0;
  420.   int high=m_numblk-1;
  421.   int mid;
  422.   for (int ni=0;;++ni) {
  423.     if (ni>(int)m_numblk) // prevent loops on unsorted invalid data
  424.       return 0;
  425.     if (low>high)
  426.       return 0;
  427.     mid=(low+high)>>1;
  428.     if (para<m_blocks[mid].paraidx)
  429.       high=mid-1;
  430.     else if (para>=m_blocks[mid].paraidx+m_blocks[mid].numpara)
  431.       low=mid+1;
  432.     else
  433.       break;
  434.   }
  435.   if (!GetBlk(mid))
  436.     return 0;
  437.   low=0;
  438.   high=m_blocks[m_curblk].numwords;
  439.   para-=m_blocks[m_curblk].paraidx;
  440.   for (int ni=0;;++ni) {
  441.     if (ni>m_blocks[m_curblk].numwords)
  442.       return 0;
  443.     if (low>high)
  444.       return 0;
  445.     mid=(low+high)>>1;
  446.     if (para<(int)m_pindex[mid])
  447.       high=mid-1;
  448.     else if (para>=(int)m_pindex[mid+1])
  449.       low=mid+1;
  450.     else
  451.       break;
  452.   }
  453.   return m_blocks[m_curblk].wordidx+mid;
  454. }
  455. int   Dict::GetStartPofWord(int index) {
  456.   if (GetWordImp(index))
  457.     return m_blocks[m_curblk].paraidx+m_pindex[index-m_blocks[m_curblk].wordidx];
  458.   return 0;
  459. }
  460. static class SimpleDictInit: public IDict::DictInit {
  461.   static IDict *create_simple_dict(RFile *fp) { return new Dict(fp); }
  462. public:
  463.   SimpleDictInit() : DictInit("DICq",create_simple_dict) { }
  464. } g_dict_init;
  465. // create a proper dictionary
  466. IDict *IDict::Create(const CString& filename,CString *errmsg) {
  467.   kilo::auto_ptr<RFile>   fp(new RFile(filename));
  468.   if (!fp->Reopen()) {
  469.     if (errmsg)
  470.       *errmsg=FileExceptionInfo(filename,GetLastError());
  471.     return NULL;
  472.   }
  473.   BYTE   sig[SIGSIZE];
  474.   if (fp->read2(sig,SIGSIZE)!=SIGSIZE) {
  475.     if (errmsg)
  476.       errmsg->Format(_T("%s: Ivalid dictionary file"),(const TCHAR*)filename);
  477.     return NULL;
  478.   }
  479.   IDict *d=NULL;
  480.   for (DictInit *di=DictInit::m_head;di;di=di->m_next)
  481.     if (memcmp(sig,di->m_sig,SIGSIZE)==0) {
  482.       d=di->m_create(fp.release());
  483.       break;
  484.     }
  485.   if (d && d->Valid())
  486.     return d;
  487.   delete d;
  488.   if (errmsg)
  489.     errmsg->Format(_T("%s: Ivalid dictionary file"),(const TCHAR*)filename);
  490.   return NULL;
  491. }
  492. IDict::DictInit   *IDict::DictInit::m_head;
  493. IDict::DictInit::DictInit(const char *sig,IDict *(*create)(RFile *)) {
  494.   m_next=m_head;
  495.   m_create=create;
  496.   strncpy(m_sig,sig,sizeof(m_sig));
  497.   m_head=this;
  498. }
  499. Buffer<wchar_t> DictParser::GetParagraphImp(int para,bool& isfirst) {
  500.   int     ws=m_dict->GetWordFromP(para);
  501.   Buffer<wchar_t>   word(m_dict->GetWordW(ws));
  502.   int     ps=m_dict->GetStartPofWord(ws);
  503.   isfirst=ps==para;
  504.   const wchar_t     *wp=word,*we=wp+word.size();
  505.   while (ps!=para && wp<we) {
  506.     while (wp<we && *wp!=L'n')
  507.       ++wp;
  508.     if (wp<we)
  509.       ++wp;
  510.     ++ps;
  511.   }
  512.   const wchar_t     *wq=wp;
  513.   while (wq<we && *wq!=L'n')
  514.     ++wq;
  515.   return Buffer<wchar_t>(wp,wq-wp);
  516. }
  517. Paragraph DictParser::GetParagraph(int docid,int para) {
  518.   bool isfirst;
  519.   Buffer<wchar_t>   txt(GetParagraphImp(para,isfirst));
  520.   Paragraph p(txt.size());
  521.   p.str=txt;
  522.   for (int j=0;j<p.str.size() && p.str[j]==L' ';++j)
  523.     p.str[j]=0xA0; // replace leading spaces with nbsp
  524.   p.cflags.Zero();
  525.   if (isfirst)
  526.     for (int i=0;i<p.str.size()-1;++i) {
  527.       if (p.str[i]==L' ' && p.str[i+1]==L' ')
  528. break;
  529.       p.cflags[i].bold=1;
  530.     }
  531.   return p;
  532. }
  533. int   DictParser::GetPLength(int docid,int para) {
  534.   bool isfirst;
  535.   return GetParagraphImp(para,isfirst).size();
  536. }
  537. DictParser  *DictParser::OpenDict(const CString& path,CString *errmsg) {
  538.   IDict   *d=IDict::Create(path,errmsg);
  539.   if (!d)
  540.     return NULL;
  541.   return new DictParser(kilo::auto_ptr<IDict>(d));
  542. }
  543. bool  DictParser::LookupReference(const wchar_t *rname,FilePos& dest) {
  544.   int   index,found;
  545.   if (m_dict->Find(rname,index,found)) {
  546.     dest.docid=0;
  547.     dest.para=m_dict->GetStartPofWord(index);
  548.     dest.off=0;
  549.     return true;
  550.   }
  551.   return false;
  552. }