用 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 应用程序。
参考资料
您可以参阅本文在 developerWorks 全球站点上的 英文原文。
从 alphaWorks 下载 the Dictionary and Thesaurus API for Java。
本系列文章的 第 1 部分(developerWorks, 2004 年 9 月)介绍了 JADT 和概念,讨论了 API 中包含的各种类和设计,以及 JADT 的可用特性。
从 alphaWorks 下载 IBM XML4J。
在 developerWorks Java 技术专区可以找到数百篇有关 Java 各个方面的技术文章。
请访问 Developer Bookstore,获得技术书籍的完整列表,其中包括数百本 Java 相关主题的图书。
是否对无需通常的高成本入口点(entry point)或短期评估许可证的 IBM 测试产品感兴趣? developerWorks 工具包订阅 为 WebSphere、DB2、 Lotus、Rational 和 Tivoli 产品提供了低成本的 12 个月单用户许可证,包括基于 Eclipse 的 WebSphere Studio IDE,用于开发、测试、评估和展示您的应用程序。
关于作者
Rakesh Midha 是 Bangalore 的 IBM 软件试验室的软件工程师。他目前从事 IBM WebSphere 业务组件开发工作。他有 5 年在多平台和各种关系数据库系统(像 DB2 UDB、Oracle、MySQL 和 Microsoft SQL Server)上进行 Java 和 C++ 服务器端编程的经验。他的专长领域包括在银行、金融、编目行业以及订单和仓储管理系统等领域设计和开发独立的和 n 层分布式系统。他从 Chandigarh 的 Punjab 大学获得了电子工程学士学位,是 Java 字典和辞典方面的技术专家,这项技术是由 IBM alphaWorks 启动的