Skip to content

Commit cb6d24c

Browse files
committed
Revert breaking changes and restore compatibility; update tests for Bigram and WordDictionary
1 parent 539d714 commit cb6d24c

10 files changed

Lines changed: 173 additions & 110 deletions

File tree

src/Lucene.Net.Analysis.SmartCn/Hhmm/AbstractDictionary.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ namespace Lucene.Net.Analysis.Cn.Smart.Hhmm
3333
internal abstract class AbstractDictionary
3434
{
3535
// LUCENENET specific: cached GB2312 encoding to avoid repeated calls to Encoding.GetEncoding("GB2312")
36-
protected static readonly Encoding gb2312Encoding = Encoding.GetEncoding("GB2312");
36+
protected static readonly Encoding gb2312Encoding = Encoding.GetEncoding("GB2312",
37+
EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback);
38+
3739

3840
/// <summary>
3941
/// First Chinese Character in GB2312 (15 * 94)

src/Lucene.Net.Analysis.SmartCn/Hhmm/BigramDictionary.cs

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -258,11 +258,19 @@ private void Load(string dictRoot)
258258
/// <exception cref="IOException">If there is a low-level I/O error</exception>
259259
public virtual void LoadFromFile(string dctFilePath)
260260
{
261+
int i, cnt, length, total = 0;
262+
261263
// The file only counted 6763 Chinese characters plus 5 reserved slots 3756~3760.
262264
// The 3756th is used (as a header) to store information.
263265

266+
Span<int> buffer = stackalloc int[3];
267+
string tmpword;
268+
264269
// LUCENENET: Removed buffer and intBuffer arrays since BinaryReader handles reading values directly in a more type-safe and readable way.
265270
// LUCENENET specific - refactored constants for clarity
271+
272+
// The 3756th position (using 1-based counting) corresponds to index 3755 (using 0-based indexing)
273+
// This matches the original Java implementation which used 3755 + GB2312_FIRST_CHAR in the condition
266274
const int HEADER_POSITION = 3755;
267275
const int MAX_VALID_LENGTH = 1000;
268276

@@ -271,39 +279,43 @@ public virtual void LoadFromFile(string dctFilePath)
271279
using var reader = new BinaryReader(dctFile);
272280

273281
// GB2312 characters 0 - 6768
274-
for (int i = GB2312_FIRST_CHAR; i < GB2312_FIRST_CHAR + CHAR_NUM_IN_FILE; i++)
282+
for (i = GB2312_FIRST_CHAR; i < GB2312_FIRST_CHAR + CHAR_NUM_IN_FILE; i++)
275283
{
276284

277-
string currentStr = GetCCByGB2312Id(i);
278-
int cnt;
285+
string currentStr = GetCCByGB2312Id(i);
286+
// if (i == 5231)
287+
// System.out.println(i);
279288
try
280289
{
281-
cnt = reader.ReadInt32(); // LUCENENET: Use BinaryReader methods instead of ByteBuffer
290+
cnt = reader.ReadInt32(); // LUCENENET: Use BinaryReader to decode little endian instead of ByteBuffer, since this is the default in .NET
282291
}
283292
catch (EndOfStreamException)
284293
{
285-
// Reached end of file
294+
// Test dictionary files contain fewer entries than production files
295+
// Breaking here is normal and expected behavior for test files
286296
break;
287297
}
288298

289299
if (cnt <= 0)
290300
{
291301
continue;
292302
}
293-
294-
for (int j = 0; j < cnt; j++)
303+
total += cnt;
304+
int j = 0;
305+
while (j < cnt)
295306
{
296-
// LUCENENET: Use BinaryReader methods instead of ByteBuffer
297-
int frequency = reader.ReadInt32();
298-
int length = reader.ReadInt32();
299-
reader.ReadInt32(); // Skip handle value (unused)
307+
// LUCENENET: Use BinaryReader to decode little endian instead of ByteBuffer, since this is the default in .NET
308+
buffer[0] = reader.ReadInt32(); // frequency
309+
buffer[1] = reader.ReadInt32(); // length
310+
buffer[2] = reader.ReadInt32(); // Skip handle value (unused)
300311

312+
length = buffer[1];
301313
if (length > 0 && length <= MAX_VALID_LENGTH && dctFile.Position + length <= dctFile.Length)
302314
{
303-
byte[] lchBuffer = reader.ReadBytes(length); // LUCENENET: Use BinaryReader methods instead of ByteBuffer
315+
byte[] lchBuffer = reader.ReadBytes(length); // LUCENENET: Use BinaryReader to decode little endian instead of ByteBuffer, since this is the default in .NET
304316

305317
//tmpword = new String(lchBuffer, "GB2312");
306-
string tmpword = gb2312Encoding.GetString(lchBuffer); // LUCENENET specific: use cached encoding instance from base class
318+
tmpword = gb2312Encoding.GetString(lchBuffer); // LUCENENET specific: use cached encoding instance from base class
307319
//tmpword = Encoding.GetEncoding("hz-gb-2312").GetString(lchBuffer);
308320

309321

@@ -324,11 +336,13 @@ public virtual void LoadFromFile(string dctFilePath)
324336
// bigramStringTable[index] = tmpword;
325337

326338
}
327-
frequencyTable[index] += frequency;
339+
frequencyTable[index] += buffer[0];
328340
}
329341
}
342+
j++;
330343
}
331344
}
345+
// log.info("load dictionary done! " + dctFilePath + " total:" + total);
332346
}
333347
private int GetAvaliableIndex(long hashId, ReadOnlySpan<char> carray)
334348
{

src/Lucene.Net.Analysis.SmartCn/Hhmm/WordDictionary.cs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -345,21 +345,26 @@ private void SaveToObj(FileInfo serialObj)
345345
/// <exception cref="IOException">If there is a low-level I/O error.</exception>
346346
private int LoadMainDataFromFile(string dctFilePath)
347347
{
348-
int total = 0;
348+
int i, cnt, length, total = 0;
349349

350350
// The file only counted 6763 Chinese characters plus 5 reserved slots (3756~3760).
351351
// The 3756th is used (as a header) to store information.
352352

353+
Span<int> buffer = stackalloc int[3];
354+
353355
// LUCENENET: Removed buffer and intBuffer arrays since BinaryReader handles reading values directly in a more type-safe and readable way.
354356
// LUCENENET: Use BinaryReader to simplify endian conversion and stream reading.
355357

356358
using (var dctFile = new FileStream(dctFilePath, FileMode.Open, FileAccess.Read))
357359
using (var reader = new BinaryReader(dctFile))
358360
{
359361
// GB2312 characters 0 - 6768
360-
for (int i = GB2312_FIRST_CHAR; i < GB2312_FIRST_CHAR + CHAR_NUM_IN_FILE; i++)
362+
for (i = GB2312_FIRST_CHAR; i < GB2312_FIRST_CHAR + CHAR_NUM_IN_FILE; i++)
361363
{
362-
int cnt = reader.ReadInt32(); // LUCENENET: Use BinaryReader methods instead of ByteBuffer
364+
// if (i == 5231)
365+
// System.out.println(i);
366+
367+
cnt = reader.ReadInt32(); // LUCENENET: Use BinaryReader to decode little endian instead of ByteBuffer, since this is the default in .NET
363368

364369
if (cnt <= 0)
365370
{
@@ -371,16 +376,21 @@ private int LoadMainDataFromFile(string dctFilePath)
371376
wordItem_charArrayTable[i] = new char[cnt][];
372377
wordItem_frequencyTable[i] = new int[cnt];
373378
total += cnt;
374-
375-
for (int j = 0; j < cnt; j++)
379+
int j = 0;
380+
while (j < cnt)
376381
{
377-
// LUCENENET: Use BinaryReader methods instead of ByteBuffer
378-
int frequency = reader.ReadInt32();
379-
int length = reader.ReadInt32();
380-
reader.ReadInt32(); // Skip handle (unused)
382+
// wordItemTable[i][j] = new WordItem();
383+
384+
// LUCENENET: Use BinaryReader to decode little endian instead of ByteBuffer, since this is the default in .NET
385+
buffer[0] = reader.ReadInt32(); // frequency
386+
buffer[1] = reader.ReadInt32(); // length
387+
buffer[2] = reader.ReadInt32(); // handle
388+
389+
// wordItemTable[i][j].frequency = buffer[0];
381390

382-
wordItem_frequencyTable[i][j] = frequency;
391+
wordItem_frequencyTable[i][j] = buffer[0];
383392

393+
length = buffer[1];
384394
if (length > 0)
385395
{
386396
byte[] lchBuffer = reader.ReadBytes(length);
@@ -389,8 +399,11 @@ private int LoadMainDataFromFile(string dctFilePath)
389399
}
390400
else
391401
{
402+
// wordItemTable[i][j].charArray = null;
392403
wordItem_charArrayTable[i][j] = null;
393404
}
405+
// System.out.println(indexTable[i].wordItems[j]);
406+
j++;
394407
}
395408

396409
string str = GetCCByGB2312Id(i);

src/Lucene.Net.Analysis.SmartCn/Lucene.Net.Analysis.SmartCn.csproj

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,13 @@
3838
<NoWarn>$(NoWarn);1591;1573</NoWarn>
3939
</PropertyGroup>
4040

41-
42-
4341
<ItemGroup>
4442
<EmbeddedResource Include="Hhmm/*.mem" Label="Dict Test Data" />
4543
<EmbeddedResource Include="**/*.txt" Exclude="bin/**/*;obj/**/*" Label="Text Test Data" />
4644
</ItemGroup>
4745

4846
<ItemGroup>
49-
<ProjectReference Include="..\dotnet\Lucene.Net.ICU\Lucene.Net.ICU.csproj" />
47+
<ProjectReference Include="..\dotnet\Lucene.Net.ICU\Lucene.Net.ICU.csproj" />
5048
<ProjectReference Include="..\Lucene.Net\Lucene.Net.csproj" />
5149
<ProjectReference Include="..\Lucene.Net.Analysis.Common\Lucene.Net.Analysis.Common.csproj" />
5250
</ItemGroup>
@@ -65,8 +63,7 @@
6563
</ItemGroup>
6664

6765
<ItemGroup>
68-
<InternalsVisibleTo Include="Lucene.Net.Tests.Analysis.SmartCn" />
69-
</ItemGroup>
70-
66+
<InternalsVisibleTo Include="Lucene.Net.Tests.Analysis.SmartCn" />
67+
</ItemGroup>
7168

7269
</Project>

src/Lucene.Net.Tests.Analysis.SmartCn/DictionaryTests.cs

Lines changed: 0 additions & 72 deletions
This file was deleted.
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
using J2N;
2+
using Lucene.Net.Analysis.Cn.Smart;
3+
using Lucene.Net.Analysis.Cn.Smart.Hhmm;
4+
using Lucene.Net.Attributes;
5+
using Lucene.Net.Util;
6+
using NUnit.Framework;
7+
using System;
8+
using System.IO;
9+
using Assert = Lucene.Net.TestFramework.Assert;
10+
11+
namespace Lucene.Net.Analysis.Cn.Smart.Hhmm
12+
{
13+
/*
14+
* Licensed to the Apache Software Foundation (ASF) under one or more
15+
* contributor license agreements. See the NOTICE file distributed with
16+
* this work for additional information regarding copyright ownership.
17+
* The ASF licenses this file to You under the Apache License, Version 2.0
18+
* (the "License"); you may not use this file except in compliance with
19+
* the License. You may obtain a copy of the License at
20+
*
21+
* http://www.apache.org/licenses/LICENSE-2.0
22+
*
23+
* Unless required by applicable law or agreed to in writing, software
24+
* distributed under the License is distributed on an "AS IS" BASIS,
25+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26+
* See the License for the specific language governing permissions and
27+
* limitations under the License.
28+
*/
29+
30+
[LuceneNetSpecific]
31+
public class TestBuildDictionary : LuceneTestCase
32+
{
33+
private DirectoryInfo tempDir;
34+
35+
public override void OneTimeSetUp()
36+
{
37+
base.OneTimeSetUp();
38+
tempDir = CreateTempDir("smartcn-data");
39+
AnalyzerProfile.ANALYSIS_DATA_DIR = tempDir.FullName;
40+
using (var zipFileStream = typeof(TestBuildDictionary).FindAndGetManifestResourceStream("custom-dictionary-input.zip"))
41+
{
42+
TestUtil.Unzip(zipFileStream, tempDir);
43+
}
44+
}
45+
46+
public override void OneTimeTearDown()
47+
{
48+
AnalyzerProfile.ANALYSIS_DATA_DIR = null; // Ensure this test data is not loaded for other tests
49+
base.OneTimeTearDown();
50+
}
51+
52+
[Test]
53+
public void TestBigramDictionary()
54+
{
55+
// First test - builds and loads dictionary from .dict file
56+
BigramDictionary bigramDict = BigramDictionary.GetInstance();
57+
CheckBigramDictionary(bigramDict);
58+
59+
// Ensure .mem file was created
60+
string memFile = System.IO.Path.Combine(tempDir.FullName, "bigramdict.mem");
61+
Assert.IsTrue(File.Exists(memFile), "Memory file should be created after first load");
62+
63+
// Delete the original .dict file
64+
string dictFile = System.IO.Path.Combine(tempDir.FullName, "bigramdict.dct");
65+
if (File.Exists(dictFile))
66+
{
67+
File.Delete(dictFile);
68+
}
69+
70+
// Second test - should load from .mem file now
71+
bigramDict = BigramDictionary.GetInstance();
72+
CheckBigramDictionary(bigramDict);
73+
}
74+
75+
private static void CheckBigramDictionary(BigramDictionary bigramDict)
76+
{
77+
Assert.AreEqual(10, bigramDict.GetFrequency("啊hello".AsSpan()), "Frequency for '啊hello' is incorrect.");
78+
Assert.AreEqual(20, bigramDict.GetFrequency("阿world".AsSpan()), "Frequency for '阿world' is incorrect.");
79+
}
80+
81+
[Test]
82+
public void TestWordDictionary()
83+
{
84+
// First test - builds and loads dictionary from .dict file
85+
WordDictionary wordDict = WordDictionary.GetInstance();
86+
CheckWordDictionary(wordDict);
87+
88+
// Ensure .mem file was created
89+
string memFile = System.IO.Path.Combine(tempDir.FullName, "coredict.mem");
90+
Assert.IsTrue(File.Exists(memFile), "Memory file should be created after first load");
91+
92+
// Delete the original .dict file
93+
string dictFile = System.IO.Path.Combine(tempDir.FullName, "coredict.dct");
94+
if (File.Exists(dictFile))
95+
{
96+
File.Delete(dictFile);
97+
}
98+
99+
// Second test - should load from .mem file now
100+
wordDict = WordDictionary.GetInstance();
101+
CheckWordDictionary(wordDict);
102+
}
103+
104+
private static void CheckWordDictionary(WordDictionary wordDict)
105+
{
106+
Assert.AreEqual(30, wordDict.GetFrequency("尼".ToCharArray()), "Frequency for '尼' is incorrect.");
107+
Assert.AreEqual(0, wordDict.GetFrequency("missing".ToCharArray()), "Expected frequency 0 for unknown word.");
108+
}
109+
}
110+
}
Binary file not shown.

0 commit comments

Comments
 (0)