用 Java 字典和辞典 API 使 Java 用户可以使用您的单词参考
Rakesh Midha (mrakesh@in.ibm.com)
软件工程师, IBM 软件实验室,Bangalore
2004 年 10 月
在介绍 JADT 的第 1 篇文章中,介绍了来自 alphaWorks 的一套类库,用于在 Java 应用程序中访问语言特性。接下来,Rakesh Midha 在本文中给出了 JADT 体系结构和 API 的详细说明。他还通过剖析一个驱动程序的实现示例,向您展示了如何把这个 API 的实现作为驱动程序启动。
在本系列文章的 第 1 部分 中,您学到了 JADT 的一些基础知识,JADT 提供了透明地以 Java 为中心的访问字典或非结构化单词以及它们的有关信息的方法。JADT 的主要目标是为字典提供者提供一个可以依据的标准技术,以便他们支持 Java 平台。第 1 部分从开发人员的角度介绍了 JADT。在本文中,我要给出实现的细节,重点从字典开发商的角度考虑。我会带您了解 JADT 以及 JADT 的体系结构的细节,并提供 JADT 中包含的所有类和接口的信息。
在 JADT 示例驱动程序实现的帮助下,我将讨论驱动程序必需的接口实现。之后,您将对 JADT 驱动程序和提供驱动程序的方法有了一个整体的印象。
JADT 体系结构
JADT 体系结构为任何类型的后端数据提供了基于驱动程序的访问。字典提供者实现驱动程序端接口,为字典提供一个访问点,任何使用这个字典的程序,将使用字典服务的用户端接口。结果就是,您可以方便地把 JADT 体系结构与各种开发商和第三方驱动程序集成在一起。如图 1 所示,专用的语言数据总是通过 JADT Driver
实现与 JADT 保持分离:
图 1. JADT 体系结构
JADT 开发商的角色 如果您想用 JADT 公开您的字典或辞典,那么只需提供 JADT 的实现即可。JADT 驱动程序位于开发商数据和 Java 程序员之间,允许开发商在不完全公开他们的数据和数据格式的情况下,对外提供服务。反过来。Java 程序员也不必深入某个开发商专用的数据和数据格式,就可以使用开发商的数据。 JADT 驱动程序开发商可以使用 JADT 接口,并根据它们自己的习惯实现这些接口。一旦驱动程序就绪,可以访问您的数据,那么就可以在您的站点启用它了。到这个站点的链接,可以作为 JADT 访问页面中的一个线程来使用。 JADT 用一致的方式在所有数据辞典上工作,这一点很重要,所以实现所有符合 JADT 所设标准的 JADT 接口和方法,是开发商的责任。JADT 的 JADT Javadocs 和接口定义文档非常清楚地公开了接口和方法的目标。 |
JADT 实现近观
JADT 定义了多组接口和类。有些接口是辅助性的,有些是可选的,这取决于您的驱动程序所要支持的功能。如果要完整地支持 JADT,那么就必须按照 Javadocs 中所定义的规范,实现所有接口。在这一节里,我将给出所有这些接口和类实现的细节。
完整的 JADT 接口和类集,如图 2 中的类图所示:
图 2. JADT 类图
下面我要介绍各种类和接口。
JADTDriverFactoryManager
JADTDriverFactory
对象的基本控制类是 com.ibm.jadt.JADTDriverFactoryManager
,这个类就像用户和可用的驱动程序工厂之间的连接。可以用它取得 Driver
的实例,然后再用实例得到某个驱动程序实现的一个或多个 JADT 服务。
在 JADTDriverFactory
载入之后,由 JADTDriver
提供者负责注册 JADTDriverFactory
,所以 JADT 用户还可以在任何时候,通过调用 Class.forName(Driver_factory_class);
,显式地装入 JADTDriver
。
驱动程序工厂类的名称应当随 JADTDriverFactoryName
一起交给用户,用户使用这个类的静态方法 getJADTDriverFactory(String factoryName)
,就可以得到 getJADTDriverFactory
。您可以调用静态 registerJADTDriverFactory (JADTDriverFactory factory)
方法来注册它。
JADT 带有下列接口,必须实现这些接口:
com.ibm.jadt. JADTDriverFactory
com.ibm.jadt.JADTDriver
JADTDriverFactory
com.ibm.jadt.JADTDriverFactory
工厂接口用于建立驱动程序实例,应当由驱动程序的实现者注册到工厂控制器。由于 JADT 是基于驱动程序的产品,所以 JADTDriver
的构造应当一直通过工厂。所有驱动程序实现者都应当实现这个接口。在 DriverFactory
类的静态块里,工厂应当用 FactoryManager
注册,如清单 1 所示:
清单 1. 注册工厂的静态方法
public class JADTSampleDriverFactory implements JADTDriverFactory
{
private static JADTSampleDriverFactory factory;
private Properties props;
private JADTSampleDriver driver;
static {
try{
if(factory==null)
factory=new JADTSampleDriverFactory();
JADTDriverFactoryManager.registerJADTDriverFactory(factory);
}
catch(Exception ex){
//handle exception
}
}
}
|
除了向管理器注册工厂之外, JADTSampleDriverFactory
类还必须实现 createJADTDriver()
方法。可以用 createJADTDriver()
方法把载入和数据访问策略(或者其他任何类型的访问属性)分开。例如,清单 2 显示了您如何才能设置要初始化的工厂数量,这由 loading_strategy
属性决定:
清单 2. 载入策略示例
public JADTDriver createJADTDriver() throws JADTException{
if(this.getProperty("loading_strategy")!=null&&
this.getProperty("loading_strategy").equals("multiple")){
if(this.getProperty("JADTSampleDriverDir")==null)
return driver=new JADTSampleDriver();
else return driver=new JADTSampleDriver
(this.getProperty("JADTSampleDriverDir"));
}
return new JADTSampleDriver();
else
{
if(this.getProperty("JADTSampleDriverDir")!=null)
if(driver==null)
driver=new JADTTextDriver(this.getProperty("JADTSampleDriverDir"));
else
if(driver==null)
driver=new JADTSampleDriver();
return driver;
}
}
|
驱动程序使用的所有属性,都必须编制文档,并提供驱动程序使用指南。这个接口方法的其余部分是自解释的,所以可以很容易地实现。
JADTDriver 接口
com.ibm.jadt.JADTDriver
接口是所有驱动程序都必须实现的主驱动程序接口。它是由 JADTDriverFactory
建立的,是向您提供驱动程序服务访问的类,服务包括字典、辞典、拼写检查器、单词表、翻译器、语法检测器,以及词变位器。
这个接口代表具体厂商的驱动程序。除此之外,相同的驱动程序厂商还可以为不同的语言提供服务。
JADTDriver
类必须实现基本的构造函数方法,构造函数方法可以用来指定属性,例如数据库、目录或者数据访问方法。还可以用构造函数初始化数据库的基本连接。类的结构如清单 3 所示:
清单 3. JADTSampleDriver 基本示例代码
public class JADTSampleDriver implements JADTDriver
{
private Properties props;
public JADTSampleDriver()
{
props=new Properties();
}
public JADTSampleDriver(String datapath)
{
props=new Properties();
setProperty("JADTSampleDriverDir",filepath);
}
/*
Rest all methods
*/
}
|
JADTDriver
类还提供了对各种服务的访问,所以这个类实现了所有类都使用的公共 get>Service<
方法。 例如,getDictionary()
方法看起来会像清单 4:
清单 4. 字典服务方法实现
public Dictionary getDictionary(String langFrom,String langTo) throws JADTException
{
try{
if(getProperty("JADTSampleDriverDir")!=null){
return new SampleDictionary
(langFrom,langTo,getProperty("JADTSampleDriverDir"));
}
else return new SampleDictionary(langFrom,langTo);
}
catch(JADTException exp){
JADTException ex1=new JADTException
("DictionaryFailure","Could not create dictionary");
ex1.setNextException(exp);
throw ex1;
}
}
|
此外,清单 5 中显示的方法也必须用类似的风格实现:
清单 5. 其他服务方法列表
public WordBook getWordBook
(String lang)throws JADTException;
public SpellChecker getSpellChecker
(String lang)throws JADTException;
public WordLister getWordlister
(String lang)throws JADTException;
public Translator getTranslator
(String langFrom,String langTo)throws JADTException;
public GrammarChecker getGrammarChecker
(String lang)throws JADTException;
public Anagrammizer getAnagrammizer
(String lang)throws JADTException;
|
使用数据结构
JADT 还提供了另外一组类和接口,用于提供组织数据的一般性方法。因为所有服务都使用数据库结构,所以 JADT 用户知道数据结构是非常重要的。在下一节中,我会回顾一下数据结构。
单词表接口
WordList
接口代表单词的列表。因为它就是单词的容器,所以它并不从后端资源提取数据。 WordList
一般用于传递、获取、设置单词,或者包含某些单词。
WordList
类的结构如清单 6 所示:
清单 6. SampleWordlist 实现
public class SampleWordList implements WordList
{
private Vector words;
private int currentPos=0;
public TextWordList() {/*default constructor*/}
public int size(){return words.size();}
public void addWord(Word word)
{
words.addElement(word);
}
public void addWords(Vector vec)
{
words.addAll(vec);
}
public Vector getAllWords()
{
return words;
}
public void Start()
{
currentPos=0;
}
public boolean hasMoreWords()
{
if(currentPos==words.size()) return false;
else return true;
}
public WordList findWithPrefix(String find)
{
/* implementation */
}
public WordList findWithSuffix(String find)
{
/* implementation */
}
public WordList findWithSubstring(String find)
{
/* implementation */
}
}
|
单词接口
Word
接口是字符的组合,代表母语使用者能够识别的语言单元。 Word
接口包装了单词的有关信息。保存在 Word
接口中的信息包括:拼写、类型、来源、发音和记录。
因为这个接口以真实格式表示单词,所以实现应当提供一个容器,容器带有必需的 get 和 set 方法。清单 7 显示的信息对于形成单词是必需的:
清单 7. 示例单词属性
private String word;
private String type;
private DictionaryRecord meaning;
private DictionaryRecord synonym;
private DictionaryRecord antonym;
private String pronunciation;
public static final int MEANINGS=1;
public static final int SYNONYMS=2;
public static final int ANTONYMS=3;
private String source="";
|
这些字段可以通过它们的 get 和 set 方法来访问。
JADT 服务
JADT 为各种服务对象定义了接口。在下一节中,我将逐一介绍当前 JADT 版本中包含的服务对象。
字典接口
Dictionary
接口代表参考书,参考书里包含按字母顺序排列的单词表。每个条目都包含释义、类型、发音、来源和用法。
Dictionary
的实现应当包含字典接口中的所有方法,但不包含构造函数,同时要用恰当的载入机制。载入机制可以是消极(lazy),一次性(single shot),或者多重装入(multiple load) 策略,具体取决于属性,而且可以修正载入机制。
例如,构造函数应当从数据库或文件中一次性载入数据,然后把数据保存在字典实现存储库中,如清单 8 所示:
清单 8. SampleDictionary 类
public class SampleDictionary implements Dictionary
{
private Hashtable words;
private String langFrom;
private String langTo;
public static final int LOADWHOLEONE=1;
public static final int LOADWHOLEMULTIPLE=2;
public static final int RUNTIME=3;
public static final int INDEXING=4;
private int technique;
public SampleDictionary(String strLangFrom,String strLangTo,
String dir,int method) throws JADTException{
/*
Load the data from storage unit, according to strategy defined.
*/
}
/*
Rest all methods
*/
}
|
余下的方法只是 getMeaning()
方法的变体,负责从载入的数据中提取单个或多个单词的含义。清单 9 显示了一个例子:
清单 9. 示例 getMeaning 方法
public com.ibm.jadt.DictionaryRecord getMeaning(Word word)
{
if(words.containsKey(word.toString().toLowerCase()))
return ((Word)words.get(word.toString().toLowerCase())).getRecord(TextWord.MEANINGS);
else return null;
}
|
辞典组件
WordBook
是一个服务组件,它提供所有相关单词的信息。这些单词可以根据用法、来源、发音等进行关联。JADT 的当前版本提供了实现同义词、反义词、上位词、下位词、整体名词、部分名词的接口,我们在 第 1 部分中已经讨论过。
实现需要让方法可以访问所有这类数据,建立从数据源载入数据的必要机制,如清单 10 所示:
清单 10. 示例辞典实现
public class SampleWordBook implements WordBook
{
private Hashtable wordsSyn;
/* private hashtable for rest all services data, this act like a repository for data */
public SampleWordBook(){
/* loads required data into repository*/
}
public DictionaryRecord getSynonyms(Word word) throws JADTException {
WordList syn=(WordList)wordsSyn.get(word.toString());
if(syn==null||syn.size()==0)
return null;
DictionaryRecord first=null;
DictionaryRecord curr=null;
Enumeration enum=syn.getAllWords().elements();
while (enum.hasMoreElements())
{
if(first==null)
{
curr=new DictionaryRecord();first=curr;
}
else
{
curr.setNextRecord(new DictionaryRecord());
curr=curr.getNextRecord();
}
curr.setWordName(((Word)enum.nextElement()).toString());
}
return first;
}
/*
Similarly implement rest all services methods
*/
}
|
其他服务
其余的服务我已经在第 1 部分详细介绍过。这里是一个快速回顾,包括附加的实现细节。
拼写检查器
SpellChecker
类用于捕获拼写错误的单词。这个类在执行方面没有什么新鲜东西。
它把数据保存在私有散列表中,并实现了其他方法。它还实现了检查拼写的算法。这个服务类与其他服务的实现方式一样。
单词表类
WordLister
可用于从后端资源取得单词。JADT WordLister
还提供了一个选项,可以查找符合某个规则(例如相似前缀、相似后缀和公共子字符串)的所有单词。
WordLister
类把数据保存在私有散列表中,并实现了其他方法。它还实现了检查拼写的算法。这个服务类与其他服务的实现方式一样。
词变位器类
Anagrammizer
可以给出单词或短语的所有变位形式,对于文字游戏应用程序来说会非常有用。这个类还实现了得到单词的变位词的算法。
语法检查器类
GrammarChecker
检查单词在句子中的排列,检查单词在特定上下文中的用法。它还实现了检查语法的算法。这个服务类与其他服务的实现方式一样。
翻译器类
Translator
类用于把单词或消息从一种语言转换成另一种语言。这个特性可以用在本地化和国际化实现上。利用这个特性,用一种语言编写的资源绑定文件可以被转换成另外一种语言。
Translator
实现了把单词和句子从一种语言翻译成另外一种语言的算法。这个服务类与其他服务的实现方式一样。
汇总
一旦您已经实现了所有的服务接口和驱动程序类,您就可以把所有类捆绑在一个包里。最后的包结构看起来类似于清单 11:
清单 11. JADTDriver 包结构
com/ibm/jadt
Anagrammizer
Dictionary
DictionaryRecord
GrammarChecker
JADTDriver
JADTDriverFactory
JADTDriverFactoryManager
JADTException
SpellChecker
Translator
Word
WordBook
WordList
WordLister
com/ibm/jadtdrivers/SampleDriver
JADTSampleDriver
JADTSampleDriverFactory
SampleWord
com/ibm/jadtdrivers/SampleDriver/anagrammizer
SampleAnagrammizer
com/ibm/jadtdrivers/SampleDriver/dictionary
SampleDictionary
com/ibm/jadtdrivers/SampleDriver/spellchecker
SampleSpellChecker
com/ibm/jadtdrivers/SampleDriver/wordbook
SampleWordBook
com/ibm/jadtdrivers/SampleDriver/wordlist
SampleWordList
SampleWordLister
|
把这些文件捆绑成一个 JAR 文件,您就可以部署它了。
结束语
通过本系列文章,您对于 JADT 技术有了全面的了解。这篇文件介绍了基本的 JADT 结构,以及如何使用不同的 JADT 服务和组件建立使用字典和其他与单词有关的特性的 Java 应用程序。
参考资料
关于作者 Rakesh Midha 是 Bangalore 的 IBM 软件试验室的软件工程师。他目前从事 IBM WebSphere 业务组件开发工作。他有 5 年在多平台和各种关系数据库系统(像 DB2 UDB、Oracle、MySQL 和 Microsoft SQL Server)上进行 Java 和 C++ 服务器端编程的经验。他的专长领域包括在银行、金融、编目行业以及订单和仓储管理系统等领域设计和开发独立的和 n 层分布式系统。他从 Chandigarh 的 Punjab 大学获得了电子工程学士学位,是 Java 字典和辞典方面的技术专家,这项技术是由 IBM alphaWorks 启动的 |