使用 Simple ODF API 编写自己的 GTD 时间管理工具

IBMdW 发布于 2011/06/24 09:48
阅读 819
收藏 0

简介: 本文介绍了 ODF 文档标准及其开发工具 Simple ODF API,并以一个时间管理小工具 Co-Thinking 的开发过程为实例,展示了 Simple ODF API 的开发功能和使用方法。

Open Document Format 开放文档格式

2002 年,为了改变办公软件领域中因私有文档格式带来的软件之间彼此封闭、文档格式不兼容的状况,结构化信息标准推进组织(OASIS)启动了开发 Open Document Format(ODF)开放文档格式标准的项目。ODF 采用半结构化数据的通用语言 XML 来描述,涵盖了字处理文档、电子表格、演示文稿、绘图、图表以及公式等多种文档类型。这种开放的纯文本格式,改变了办公文档对特定厂商、特定软件产品的依 赖,使来自不同平台、不同程序的文件可以自由地交换信息,提高了办公效率,降低了用户的业务风险。

2006 年,国际标准化组织(ISO)接受 ODF 为国际标准(ISO/IEC 26300)。ODF 同时得到了 IBM、Oracle、Redhat 等厂商的广泛支持。Lotus Symphony、OpenOffice、LibreOffice、Google docs、Zoho docs、Microsoft Office 2007/2010 等办公软件均可以创建、加载和编辑 ODF 文档。

ODF Toolkit 社区与 Simple ODF API 项目

办公软件本身需求的复杂性和激烈的市场竞争,迫使每种办公软件都在不断增加新的功能和特性。ODF 作为一种开放的、统一的文档标准,必须逐渐涵盖对这些新功能、新特性的支持。这导致 ODF 标准本身越来越复杂。2011 年 3 月 17 日发布的 ODF 1.2 最新版已经超过了 1000 页。如此庞杂的内容使得普通开放者对 ODF 文档应用望而却步。

为了更好地推广 ODF 标准,IBM 公司和原 Sun 公司联合成立了 ODF Toolkit 社区。ODF Toolkit 社区致力于为开发者提供厂商中立的、开源的 ODF 文档开发工具包,以简化应用开发过程,它包括若干子项目,Simple ODF API 便是其中之一。

Simple ODF API 使用 Java 语言开发,用于以简捷的方式创建、编辑 ODF 文档,检索、抽取文档数据。Simple ODF API 在设计和开发中努力践行以下三条原则:

  • 易学(Simple Learning)在 Simple ODF API 的 主页上,不仅提供了在线版的 JavaDoc供开发者参考,而且提供了详尽的 CookBook帮助开发者了解 Simple ODF API 的每一个功能。此外,我们还结合应用场景提供了大量的 演示程序。这个演示程序列表是不断更新的。每当 Simple ODF API 有新功能加入,我们都会同时发布展示这个功能如何应用的演示程序。
  • 易用(Simple Use)作为高层次的便捷 API,Simple ODF API 的编程模型尽可能贴近开发者的编程习惯,避免涉及过多的标准的细节。开发者完全可以以面向对象的方式编写 ODF 文档处理程序。Simple ODF API 提供了强大的表格处理、文档信息检索等支持。
  • 易扩展(Simple Extension)文档处理的需求是多种多样的,Simple ODF API 无法涵盖其中每一个方面。为了便于高级开发者实现自定义需求,设计上我们提供了很多开放接口,并配以默认实现来指导开发者编程。比如,在文档信息检索 API 中,我们设计了 Selection接口,通过此接口开发者可以自由决定被检索到的文本信息被何种内容替换。它们可以是普通文本,也可以是图片,甚至是文档字段。

写作本文时,Simple ODF API 最新版本是 0.6。每个月 Simple ODF API 都会发布新版本,以保证用户需求被及时满足。Simple ODF API 使用 Apache 2.0 许可证进行分发,即使对商业应用的限制也极为宽松。

Co-Thinking 时间管理工具

Getting Things Done,即 GTD,来自于 David Allen 的著作《 Getting Things Done 》。David Allen 认为,压力并不来自事情本身,而是因为各种要做的事情囤积在大脑里,造成心理的焦虑和抵触,以致于不能有效地利用时间去完成该做的事。这种混乱的状况给人 带来了压力。GTD 的基本理念就是把需要做的事情从大脑中全部移出来,记录到 GTD 工具上。这样,大脑将不用花费精力去关注各种将要实现的想法,转而集中精力去完成正在做的事,从而达到有效利用时间,高效工作,减轻压力的目的。

一个典型的 GTD 时间管理流程如图 1 所示,大脑中朦胧的想法首先被记录到 Buckets 中,然后视具体情况被分别处理、跟踪和检查。


图 1. GTD 时间管理流程
图 1. GTD 时间管理流程

GTD 工具软件已经有很多,使用比较广泛的有:在线工具 Toodledo 和 Doit.im, MonkeyGTD, Stella GTD, ThinkingRock, 以及基于 Gmail 的 FireFox 插件 GTD Inbox for Gmail 等。

Co-Thinking 是一个采用 Simple ODF API 实现的用于帮助用户实现基于 GTD 理论的时间管理工具。与其他工具相比,Co-Thinking 最大的特点是与 ODF 文档结合紧密。Co-Thinking 全面反映了 Simple ODF API 所具备的开发能力。通过 Co-Thinking,大家会看到 Simple ODF API 能做的将不仅仅是文档的导入或导出。图 2 展示的是 Co-Thinking 的想法(Thought)处理过程。


图 2. Co-Thinking 的想法(Thought)处理界面
图 2. Co-Thinking 的想法(Thought)处理界面

Co-Thinking 没有设计私有的数据存储格式,所有的数据,包括配置信息,全部通过表格以 ODF 电子表格文档的形式存储。GTD 时间管理,不仅可以用 Co-Thinking 来做,用户还可以直接通过办公软件编辑 ODF 电子表格数据文档进行。Co-Thinking 的这一特性还使用户的时间管理数据可以方便地被导出为演示文稿,字处理文档等形式。一个很有价值的场景是:用户用 Co-Thinking 管理工作进展,在需要汇报工作进展时,可以选择性地导出特定内容为演示文稿。当然,用户还可以直接将自己的数据文档上传到 Google Docs,随时随地做时间管理,并在方便时将更新后的数据重新引入 Co-Thinking。以上功能,Co-Thinking 已经全部支持,大家可以编译源代码后运行体验。

Co-Thinking 架构设计

Co-Thinking 在整体设计上遵从 MVC 模式,其架构如图 3 所示。相对独立的视图层、控制层和模型层,降低了程序的耦合性。


图 3. Co-Thinking 架构
图 3. Co-Thinking 架构

Co-Thinking 采用 SWT/JFace 技术构建 GUI 界面,这使其拥有了同操作系统一样的显示风格。Co-Thinking 实现中使用了向导对话框、首选项、MVC 模式的表格、树等诸多 SWT 控件。具体细节可以参看 附件中的Co-Thinking 源代码。SWT/JFace 技术不属于本文重点,这里不再详细介绍。感兴趣的读者可以阅读参考资料中的文章。MVC 模式的松耦合特性也使得只要用户需要,Co-Thinking 可以方便地被迁移到其他 GUI 界面,比如 Swing,甚至是移动终端。

如图 4 所示,Co-Thinking 使用预定义好表头格式的电子表格文档作为数据文件,并使用 Simple ODF API 实现数据访问层。利用这些封装的数据访问 API,数据文件可以很容易地被 Co-Thinking 读写。


图 4. Co-Thinking 数据文件
图 4. Co-Thinking 数据文件

本文后续部分将详细介绍如何使用 Simple ODF API 来实现基本的数据访问层。

用 Simple ODF API 实现 Co-Thinking 的数据访问层

在 Co-Thinking 中,用户可以灵活选择基于预定义模板的电子表格文档作为数据文件。Co-Thinking 对数据文件的操作,包括:获取表格,读取 / 添加 / 删除行,获取 / 设置表格单元风格,获取 / 设置表格单元值,搜索文本内容等。以上操作均由数据访问层负责实现。

如图 5 所示,与数据访问层相关的类主要集中在 org.odftoolkit.simple.gtd.dal 包及其子包中。


图 5. Co-Thinking 数据访问层相关的包和类
图 5. Co-Thinking 数据访问层相关的包和类

Co-Thinking 利用电子表格文档模拟数据库操作,org.odftoolkit.simple.gtd.dal 中的 ODSConnection 类用于实现打开和关闭数据连接。


清单 1. 实现打开和关闭数据连接的 ODSConnection
        
 public class ODSConnection { 
   // open connection 
   public static SpreadsheetDocument open() throws Exception { 
     return (SpreadsheetDocument) SpreadsheetDocument 
         .loadDocument(ODSPathManager.getCurrentODSPath()); 
   } 
   // close connection 
   public static void close(SpreadsheetDocument ssDoc) throws Exception { 
     ssDoc.save(ODSPathManager.getCurrentODSPath()); 
     ssDoc.close(); 
   } 
 } 

所谓打开数据连接,实际上就是加载由 ODSPathManager 指定位置的电子表格文档并以 SpreadsheetDocument 实例的形式返回。SpreadsheetDocument 即为 Simple ODF API 对电子表格文档的对象化描述。同理,关闭数据连接就是把 SpreadsheetDocument 包含的更新后的电子表格数据通过 save() 方法重新写回到指定文件中。最后的 close() 方法负责释放不再使用的系统资源。

接下来,包 org.odftoolkit.simple.gtd.dal.daointerface 中定义了各个子模块访问数据文件的接口。包 org.odftoolkit.simple.gtd.dal.ods 给出了所有接口基于 ODF 电子表格文档的实现类。绝大多数类的实现使用了 Simple ODF API 类似的功能,这里不会一一介绍,仅以 ActionDAOImpl 和 ConfigureDAOImpl 为例来说明。

ActionDAOImpl 提供了 Co-Thinking 动作(Action)模块相关的数据访问方法实现。表 1 列出了它包含的所有方法和方法的用途。


表 1. ActionDAOImpl 的方法和用途
方法名 用途 addAction(Action) 将指定的动作(Action)对象写入数据文件 deleteAction(String) 删除由行索引指定的动作(Action) deleteActionByCreatedDate(String) 删除指定时间创建的所有动作(Action) deleteAction(Action) 删除由指定的动作(Action)对象描述的所有 Action getAllActions() 返回动作表中的全部动作(Action) updateAction(Action) 用指定的动作(Action)对象更新数据文件记录 getAllActions(String, List<Action>) 返回属于指定项目(Project)的所有动作(Action) getActionByCreatedDate(String) 获取指定时间创建的所有动作(Action) getDueTodayActions() 获取今天到期的所有动作(Action) getDueTomorrowActions() 获取明天到期的所有动作(Action) getDueWeekActions() 获取明天以后本周到期的所有动作(Action) getOverDueActions() 获取已经过期的所有动作(Action) searchActions(String) 查找包含指定内容的动作(Action)

这里我们以 addAction(Action) 为例详细介绍如何用 Simple ODF API 实现此类方法。清单 2 列出了 addAction(Action) 的实现代码。


清单 2. addAction(Action) 的实现代码
        
 public boolean addAction(Action action) { 
  …
   //read the style configure info   
   String contextStyleName = … ; 
   String topicStyleName = … ; 
   String priorityStyleName = … ; 
   String energyStyleName = … ; 
   String timeStyleName = … ; 
  
   try { 
     //open connection 
     ssDoc = ODSConnection.open(); 

     //get action table by its table name 
     Table table = ssDoc.getTableByName(Constants.ACTIONS); 

     // get table row count and get the new action's row number. 
     int rowCount = table.getRowCount(); 
     rowCount++; 

     // write action status. 
     Cell cell = table.getCellByPosition("A" + rowCount); 
     cell.setStringValue(action.getStatus()); 
     // write action name. 
     cell = table.getCellByPosition("B" + rowCount); 
     cell.setStringValue(action.getActionName()); 

     // write action context and its font style. 
     cell = table.getCellByPosition("C" + rowCount); 
     cell.setStringValue(action.getContext()); 
     cell.setCellStyleName(contextStyleName); 
     // write action topic and its font style. 
     cell = table.getCellByPosition("D" + rowCount); 
     cell.setStringValue(action.getTopic()); 
     cell.setCellStyleName(topicStyleName); 

    …

     // write notes. 
     cell = table.getCellByPosition("N" + rowCount); 
     cell.setStringValue(action.getNotes()); 
     // close connection 
     ODSConnection.close(ssDoc); 
   } catch (Exception exp) { 
     return false; 
   } 
   return true; 
 } 

addAction(Action) 在实现中主要使用了 Simple ODF API 的表格 API,具体包括:

  • 获取表格在 Simple ODF API 中,所有可以包含表格的组件都实现了 TableContainer 接口,用于实现表格添加、删除、检索等功能。单子表格文档就是一种 TableContainer。通过调用 TableContainer 提供的 getTableByName(String tableName) 方法,用户只需提供表格名称即可获取对应的表格对象 Table。代码清单 2 中,通过此方法我们获取了动作(Action)表格。
  • 获取表格行数新动作(Action)将被记录到动作(Action)表格的末尾。为了确定新动作 (Action)在动作(Action)表格中的写入位置,首先要知道当前表格的行数。Table 的 getRowCount() 方法将返回表格行数。相应的,用户还可以用 Table 的 getColumnCount() 方法返回表格列数。
  • 获取指定位置的单元格新动作(Action)的属性需要分别被写入指定行的特定单元格内。只要指 定单元格的行索引和列索引值就可以利用 Table 的 getCellByPosition() 方法获取单元格对象 Cell。这里需要说明的是,对应电子表格文档来说,Simple ODF API 是支持对其表格尺寸做自动扩展的,不存在越界问题。如果指定的单元格超出表格原有的范围,那么表格会被自动扩充,以避免越界。
  • 获取 / 设置单元格内容有了单元格对象 Cell 之后,就可以获取或设置单元格内容了。若要获取单元格的文本内容,可以直接使用 Cell 的 getDisplayText() 方法。相应的,调用 Cell 的 setStringValue() 方法可以设置相应单元格的文本内容。在 Co-Thinking 中,所有单元格的数据类型都被设置问字符串。事实上,单元格的数据类型可以多种多样,比如数字、日期、图片等等。这些数据类型也被 Cell 类支持,只需要调用相应的 get/setXXXValue 即可为单元格获取或设置不同类型的值。
  • 获取 / 设置单元格的风格Cell 提供了 get/setCellStyleName() 来支持获取或设置单元格的风格。单元格的风格限定了单元格字体、前景色等属性。随后的内容将详细介绍单元格字体的设置方法。

前文提到,在 Co-Thinking 中,所有的配置信息也都保存在电子表格文档中。图 6 展示了 Co-Thinking 的配置信息表格所包含的内容。与其他表格不同的是,配置信息表格同时包含了大量的单元格风格信息,比如字体、前景色。这些风格同 Co-Thinking 界面中看到的单元格风格是一一对应的。用户可以直接通过办公软件来更新处于完成(Done)状态的动作(Action)以何种字体显示。这就需要 Simple ODF API 提供相应的字体设置支持。


图 6. Co-Thinking 的配置信息表格
图 6. Co-Thinking 的配置信息表格

清单 3,展示了在 ConfigureDAOImpl 中,单元字体信息是如何通过 Simple ODF API 在电子表格文档和 Co-Thinking 主界面之间是映射的。


清单 3. 读取配置表格的字体信息
        
 public Map<String, Font> getFontMap() { 
   Map<String, Font> fontMap =  new HashMap<String, Font>(); 
   // key is configure item, and value is item's row index 
   Map<String, Integer> configureItemMap = new HashMap<String, Integer>(); 
   try { 
     ssDoc = ODSConnection.open(); 
     Table table = ssDoc.getTableByName(Constants.CONFIGURE); 
     int rowCount = table.getRowCount(); 
     for(int rowNumber = 1; rowNumber <= rowCount; rowNumber++) { 
       Cell cell = table.getCellByPosition("A" + rowNumber); 
       String configureItem = cell.getDisplayText(); 
       int rowIndex = cell.getRowIndex(); 
       configureItemMap.put(configureItem,    rowIndex); 
       if(configureItem.equalsIgnoreCase(Constants.COMBO_CONTEXT) || 
         configureItem.equalsIgnoreCase(Constants.COMBO_TOPIC) || 
         configureItem.equalsIgnoreCase(Constants.COMBO_PRIORITY) || 
         configureItem.equalsIgnoreCase(Constants.COMBO_ENERGY) || 
         configureItem.equalsIgnoreCase(Constants.COMBO_TIME)) { 
         fontMap.put(configureItem + "NONE", cell.getFont()); 
       } 
     } 
     Iterator<Map.Entry<String, Integer>> it = configureItemMap.entrySet().iterator(); 
     while(it.hasNext()) { 
       Map.Entry<String, Integer> entry = (Map.Entry<String, Integer>)it.next(); 
       String configureItem = entry.getKey(); 
       String configureContent = ""; 
       int rowIndex = entry.getValue(); 
       for(int colIndex = 1; ;colIndex++) { 
         Cell cell = table.getCellByPosition(colIndex, rowIndex); 
         configureContent = cell.getDisplayText(); 
         if(configureContent.equals("")) { 
           break; 
         } 
         fontMap.put(configureItem + configureContent, cell.getFont()); 
       } 
     } 
     ssDoc.close(); 
   }catch (Exception e) { 
     return null; 
   }     
   return fontMap; 
 } 

方法 getFontMap() 负责读取配置表格中各个数据项的字体配置信息,建立各数据项的字体配置索引便于其他模块使用。方法首先读取了每个数据条目默认配置值的字体信息,同时记录 了数据条目在配置表格中的位置,然后基于位置信息读取了各数据条目其它选项的字体信息。该方法使用单元格 Cell 的 getFont() 方法获取封装后的字体信息 Font。使用 Font 就可以获得包括字体名称,字体样式,划线样式,字体大小,字体颜色等详细的参数设置。表 2 给出了 Font 支持的字体属性设置方法。


表 2. Font 的方法和用途
方法名 用途 getFamilyName()
setFamilyName(String) 获取或设置字体名称 getFontStyle()
setFontStyle(FontStyle) 获取或设置字体的显示风格,比如加粗、斜体等 getTextLinePosition()
setTextLinePosition(TextLinePosition) 获取或设置字体的下划线、删除线属性 getSize(),setSize(double) 获取或设置字体大小 getColor(),setColor(Color) 获取或设置字体颜色

Co-Thinking 的 org.odftoolkit.simple.gtd.util.Utility 类提供了方法 convertSimpleFontToSWTFont() 和 convertSWTFontToSimpleFont(),以实现 Simple ODF API 中的 Font 对象与 SWT 中 Font 对象的映射。具体实现可以参考 附件中的Co-Thinking 源代码,这里不再赘述。

结束语

本以 GTD 时间管理小工具 Co-Thinking 的基本功能实现为例,向大家介绍了 Simple ODF API 的功能和用法。Co-Thinking 同时提供了演示文稿导出、GoogleDocs 同步等功能,关于这部分功能的实现方法将会在以后的文章中介绍。随着 Simple ODF API 开发进程的演进,我们会基于 Simple ODF API 提供的新功能来不断增强 Co-Thinking 的能力。

原文来自 IBM developerWorks

加载中
返回顶部
顶部