pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / contrib / analyzers / smartcn / src / java / org / apache / lucene / analysis / cn / smart / hhmm / BigramDictionary.java
1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 package org.apache.lucene.analysis.cn.smart.hhmm;
19
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.FileNotFoundException;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.ObjectInputStream;
27 import java.io.ObjectOutputStream;
28 import java.io.RandomAccessFile;
29 import java.io.UnsupportedEncodingException;
30 import java.nio.ByteBuffer;
31 import java.nio.ByteOrder;
32
33 import org.apache.lucene.analysis.cn.smart.AnalyzerProfile;
34
35 /**
36  * SmartChineseAnalyzer Bigram dictionary.
37  * @lucene.experimental
38  */
39 class BigramDictionary extends AbstractDictionary {
40
41   private BigramDictionary() {
42   }
43
44   public static final char WORD_SEGMENT_CHAR = '@';
45
46   private static BigramDictionary singleInstance;
47
48   public static final int PRIME_BIGRAM_LENGTH = 402137;
49
50   /*
51    * The word associations are stored as FNV1 hashcodes, which have a small probability of collision, but save memory.  
52    */
53   private long[] bigramHashTable;
54
55   private int[] frequencyTable;
56
57   private int max = 0;
58
59   private int repeat = 0;
60
61   // static Logger log = Logger.getLogger(BigramDictionary.class);
62
63   public synchronized static BigramDictionary getInstance() {
64     if (singleInstance == null) {
65       singleInstance = new BigramDictionary();
66       try {
67         singleInstance.load();
68       } catch (IOException e) {
69         String dictRoot = AnalyzerProfile.ANALYSIS_DATA_DIR;
70         singleInstance.load(dictRoot);
71       } catch (ClassNotFoundException e) {
72         throw new RuntimeException(e);
73       }
74     }
75     return singleInstance;
76   }
77
78   private boolean loadFromObj(File serialObj) {
79     try {
80       loadFromInputStream(new FileInputStream(serialObj));
81       return true;
82     } catch (FileNotFoundException e) {
83       e.printStackTrace();
84     } catch (IOException e) {
85       e.printStackTrace();
86     } catch (ClassNotFoundException e) {
87       e.printStackTrace();
88     }
89     return false;
90   }
91
92   private void loadFromInputStream(InputStream serialObjectInputStream)
93       throws IOException, ClassNotFoundException {
94     ObjectInputStream input = new ObjectInputStream(serialObjectInputStream);
95     bigramHashTable = (long[]) input.readObject();
96     frequencyTable = (int[]) input.readObject();
97     // log.info("load bigram dict from serialization.");
98     input.close();
99   }
100
101   private void saveToObj(File serialObj) {
102     try {
103       ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(
104           serialObj));
105       output.writeObject(bigramHashTable);
106       output.writeObject(frequencyTable);
107       output.close();
108       // log.info("serialize bigram dict.");
109     } catch (Exception e) {
110       // log.warn(e.getMessage());
111     }
112   }
113
114   private void load() throws IOException, ClassNotFoundException {
115     InputStream input = this.getClass().getResourceAsStream("bigramdict.mem");
116     loadFromInputStream(input);
117   }
118
119   private void load(String dictRoot) {
120     String bigramDictPath = dictRoot + "/bigramdict.dct";
121
122     File serialObj = new File(dictRoot + "/bigramdict.mem");
123
124     if (serialObj.exists() && loadFromObj(serialObj)) {
125
126     } else {
127       try {
128         bigramHashTable = new long[PRIME_BIGRAM_LENGTH];
129         frequencyTable = new int[PRIME_BIGRAM_LENGTH];
130         for (int i = 0; i < PRIME_BIGRAM_LENGTH; i++) {
131           // it is possible for a value to hash to 0, but the probability is extremely low
132           bigramHashTable[i] = 0;
133           frequencyTable[i] = 0;
134         }
135         loadFromFile(bigramDictPath);
136       } catch (IOException e) {
137         throw new RuntimeException(e.getMessage());
138       }
139       saveToObj(serialObj);
140     }
141   }
142
143   /**
144    * Load the datafile into this BigramDictionary
145    * 
146    * @param dctFilePath path to the Bigramdictionary (bigramdict.dct)
147    * @throws FileNotFoundException
148    * @throws IOException
149    * @throws UnsupportedEncodingException
150    */
151   public void loadFromFile(String dctFilePath) throws FileNotFoundException,
152       IOException, UnsupportedEncodingException {
153
154     int i, cnt, length, total = 0;
155     // The file only counted 6763 Chinese characters plus 5 reserved slots 3756~3760.  
156     // The 3756th is used (as a header) to store information.
157     int[] buffer = new int[3];
158     byte[] intBuffer = new byte[4];
159     String tmpword;
160     RandomAccessFile dctFile = new RandomAccessFile(dctFilePath, "r");
161
162     // GB2312 characters 0 - 6768
163     for (i = GB2312_FIRST_CHAR; i < GB2312_FIRST_CHAR + CHAR_NUM_IN_FILE; i++) {
164       String currentStr = getCCByGB2312Id(i);
165       // if (i == 5231)
166       // System.out.println(i);
167
168       dctFile.read(intBuffer);
169       // the dictionary was developed for C, and byte order must be converted to work with Java
170       cnt = ByteBuffer.wrap(intBuffer).order(ByteOrder.LITTLE_ENDIAN).getInt();
171       if (cnt <= 0) {
172         continue;
173       }
174       total += cnt;
175       int j = 0;
176       while (j < cnt) {
177         dctFile.read(intBuffer);
178         buffer[0] = ByteBuffer.wrap(intBuffer).order(ByteOrder.LITTLE_ENDIAN)
179             .getInt();// frequency
180         dctFile.read(intBuffer);
181         buffer[1] = ByteBuffer.wrap(intBuffer).order(ByteOrder.LITTLE_ENDIAN)
182             .getInt();// length
183         dctFile.read(intBuffer);
184         // buffer[2] = ByteBuffer.wrap(intBuffer).order(
185         // ByteOrder.LITTLE_ENDIAN).getInt();// handle
186
187         length = buffer[1];
188         if (length > 0) {
189           byte[] lchBuffer = new byte[length];
190           dctFile.read(lchBuffer);
191           tmpword = new String(lchBuffer, "GB2312");
192           if (i != 3755 + GB2312_FIRST_CHAR) {
193             tmpword = currentStr + tmpword;
194           }
195           char carray[] = tmpword.toCharArray();
196           long hashId = hash1(carray);
197           int index = getAvaliableIndex(hashId, carray);
198           if (index != -1) {
199             if (bigramHashTable[index] == 0) {
200               bigramHashTable[index] = hashId;
201               // bigramStringTable[index] = tmpword;
202             }
203             frequencyTable[index] += buffer[0];
204           }
205         }
206         j++;
207       }
208     }
209     dctFile.close();
210     // log.info("load dictionary done! " + dctFilePath + " total:" + total);
211   }
212
213   private int getAvaliableIndex(long hashId, char carray[]) {
214     int hash1 = (int) (hashId % PRIME_BIGRAM_LENGTH);
215     int hash2 = hash2(carray) % PRIME_BIGRAM_LENGTH;
216     if (hash1 < 0)
217       hash1 = PRIME_BIGRAM_LENGTH + hash1;
218     if (hash2 < 0)
219       hash2 = PRIME_BIGRAM_LENGTH + hash2;
220     int index = hash1;
221     int i = 1;
222     while (bigramHashTable[index] != 0 && bigramHashTable[index] != hashId
223         && i < PRIME_BIGRAM_LENGTH) {
224       index = (hash1 + i * hash2) % PRIME_BIGRAM_LENGTH;
225       i++;
226     }
227     // System.out.println(i - 1);
228
229     if (i < PRIME_BIGRAM_LENGTH
230         && (bigramHashTable[index] == 0 || bigramHashTable[index] == hashId)) {
231       return index;
232     } else
233       return -1;
234   }
235
236   /*
237    * lookup the index into the frequency array.
238    */
239   private int getBigramItemIndex(char carray[]) {
240     long hashId = hash1(carray);
241     int hash1 = (int) (hashId % PRIME_BIGRAM_LENGTH);
242     int hash2 = hash2(carray) % PRIME_BIGRAM_LENGTH;
243     if (hash1 < 0)
244       hash1 = PRIME_BIGRAM_LENGTH + hash1;
245     if (hash2 < 0)
246       hash2 = PRIME_BIGRAM_LENGTH + hash2;
247     int index = hash1;
248     int i = 1;
249     repeat++;
250     while (bigramHashTable[index] != 0 && bigramHashTable[index] != hashId
251         && i < PRIME_BIGRAM_LENGTH) {
252       index = (hash1 + i * hash2) % PRIME_BIGRAM_LENGTH;
253       i++;
254       repeat++;
255       if (i > max)
256         max = i;
257     }
258     // System.out.println(i - 1);
259
260     if (i < PRIME_BIGRAM_LENGTH && bigramHashTable[index] == hashId) {
261       return index;
262     } else
263       return -1;
264   }
265
266   public int getFrequency(char[] carray) {
267     int index = getBigramItemIndex(carray);
268     if (index != -1)
269       return frequencyTable[index];
270     return 0;
271   }
272
273 }