在前面的文章中,讲到了如何利用Groovy操作XML,来解析OFBiz项目的实体模型文件entitymodel.xml。 本文是上一篇文章的后续,重点是如何将数据模型导出到Word文件中,支持多个模块文件,并且利用Java5的多线程机制“生产者-消费者”模式,优化了文档打印部分,使得一个模块导出一个Doc文件。 源码如下:
/**
* ===================================================
* Author: rain(http://rainboyan.com)
*
* v5 2008/12/12 14:29
* - 性能优化。多线程实现,文件搜索和打印输出
* - 支持多文件搜索路径
* - 重构代码
*
* v4 2008/12/12 9:49
* - 支持多模块entitydef,各个模块分别保存
* - 导出Word文档自动保存在用户目录下,
* 文件命名:ofbiz-entitymodel-[module].doc
*
* v3 2008/12/12 9:03
* - 实现打印至Word文档
* - 增加用时统计
*
* v2 2008/12/12 1:16
* - 重构代码
* - 增加类:Entity和Field
*
* v1 2008/12/11 23:13
* - 实现了解析entitymodel.xml,打印至控制台。
* - 字段包括:
* Entity Name | Field Name | Field Type | PK | FK
* ===================================================
*/
import org.codehaus.groovy.scriptom.*
import java.util.concurrent.*
static final N_MODULES = 4
static final N_THREADS = N_MODULES + 1
static final File POISON = new File("")
static final True = 1
static final False = 0
static final wdStyleHeading1 = -2
static final wdStyleHeading2 = -3
static final wdStyleHeading3 = -4
static final wdStyleHeading4 = -5
static final wdStyleHeading5 = -6
static final wdStyleHeading6 = -7
static final wdStyleBodyText = -67
static final wdTexture10Percent = 100
static final wdTexture20Percent = 200
static final wdTexture12Pt5Percent = 125
static final wdTexture15Percent = 150
static final wdTexture17Pt5Percent = 175
static final wdTexture22Pt5Percent = 225
static final wdAlignRowLeft = 0
static final wdAlignRowCenter = 1
static final wdAlignRowRight = 2
static final wdAlignVerticalTop = 0
static final wdAlignVerticalCenter = 1
static final wdAlignVerticalJustify = 2
static final wdAlignVerticalBottom = 3
static final wdLineBreak = 6
static final wdPageBreak = 7
static final wdBulletGallery = 1
static final wdNumberGallery = 2
static final wdOutlineNumberGallery = 3
static final wdListNumberStyleArabic = 0
static final wdDoNotSaveChanges = 0
static final wdSaveChanges = -1
static final wdPromptToSaveChanges = -2
class Entity {
String name
String title
List attrs = []
}
class Attribute {
String name
String domain
Boolean isPK
Boolean isFK
}
def crawl = { roots, fileFilter, fileQueue ->
roots.each { root ->
root.eachFileRecurse { entry ->
if(entry.isFile() && entry.name.endsWith("entitymodel.xml")) {
fileQueue.put(entry)
}
}
}
}
def fileCrawler = { fileQueue, fileFilter, root ->
try {
crawl(root, fileFilter, fileQueue)
} catch(InterruptedException e) { /* fail */ }
finally {
while (true) {
try {
fileQueue.put(POISON)
println "Put POISON"
break
} catch (InterruptedException e1) { /* try again */ }
}
}
}
def getPKs = { pkeys ->
def pks = []
for (key in pkeys) {
pks << key["@field"]
}
return pks
}
def isPK = { field, pks ->
for (pk in pks) {
if (field == pk) return true
}
return false
}
def getFKs = { relations ->
def fks = []
relations.each { relation ->
def keys = relation.'key-map'
fks << keys[0]["@field-name"]
}
return fks
}
def isFK = { field, fks ->
for (key in fks) {
if (field == key) return true
}
return false
}
def printEntity = { doc, entity ->
def range = doc.Range()
range.SetRange(doc.Range().End, doc.Range().End)
range.Style = wdStyleHeading3
range.InsertAfter(entity.name)
range.InsertParagraphAfter()
range.SetRange(doc.Range().End, doc.Range().End)
range.Style = wdStyleBodyText
def table = doc.Tables.Add(range, entity.attrs.size() + 2, 6)
def row = table.Rows(1)
row.Cells.Height = 30
row.Cells.Shading.Texture = wdTexture20Percent
row.Alignment = wdAlignRowCenter
row.Cells.VerticalAlignment = wdAlignVerticalCenter
row.range.Style = wdStyleBodyText
row.Range.Bold = True
row.Cells(1).Range.InsertAfter "ENTITY NAME"
row.Cells(2).Range.InsertAfter "ATTRIBUTE NAME"
row.Cells(3).Range.InsertAfter "CHINESE"
row.Cells(4).Range.InsertAfter "PK?"
row.Cells(5).Range.InsertAfter "FK?"
row.Cells(6).Range.InsertAfter "DOMAIN"
row = table.Rows(2)
row.Cells(1).Range.InsertAfter entity.name
row.Cells(6).Range.InsertAfter entity.title
for (int i = 1; i < entity.attrs.size() + 1; i++) {
row = table.Rows(i+2)
row.Cells(1).Range.InsertAfter i.toString()
row.Cells(2).Range.InsertAfter entity.attrs[i-1].name
row.Cells(4).Range.InsertAfter entity.attrs[i-1].isPK ? "Y" : "N"
row.Cells(5).Range.InsertAfter entity.attrs[i-1].isFK ? "Y" : "N"
row.Cells(6).Range.InsertAfter entity.attrs[i-1].domain
}
}
def printFile = { file ->
def word = new ActiveXObject("Word.Application")
word.Visible = false
def module = file.getParentFile().name
println "Module: " + module
def doc = word.Documents.Add()
def template = doc.ListTemplates().Add()
def level1 = template.ListLevels(1)
level1.NumberFormat = "%1"
level1.NumberStyle = wdListNumberStyleArabic
level1.StartAt = 1
doc.Styles(wdStyleHeading3).LinkToListTemplate(template)
try {
def model = new XmlParser().parse(file)
//println model.title.text()
def entities = model.entity
println "Number of Entity: " + entities.size()
entities.each { entity ->
def e = new Entity()
def entityName = entity["@entity-name"]
def entityTitle = entity["@title"]
e.name = entityName
e.title = entityTitle
println "Entity: " + entityName + " (" + entityTitle + ")"
def fields = entity.field
def pkeys = entity.'prim-key'
def pks = getPKs(pkeys)
def relations = entity.relation
def fks = getFKs(relations)
fields.each { field ->
def attr = new Attribute()
attr.name = field["@name"]
attr.domain = field["@type"]
attr.isPK = isPK(field["@name"], pks)
attr.isFK = isFK(field["@name"], fks)
e.attrs << attr
}
printEntity doc, e
}
// Save Document
doc.SaveAs(System.properties['user.home'] + File.separator + "ofbiz-entitymodel-" + module + ".doc")
}
finally {
//word.Visible = true
// Exit
word.Quit(wdDoNotSaveChanges)
}
}
def printer = { queue, exec ->
try {
File file = queue.take()
if (file == POISON) {
println "Get POISON"
exec.shutdown()
}
else {
println "Print: " + file
printFile(file)
}
}
catch (InterruptedException e) {
Thread.currentThread().interrup() // fail
}
}
def startPrinting = { roots ->
BlockingQueue queue = new LinkedBlockingQueue(10)
FileFilter filter = { return true } as FileFilter
static Executor exec = Executors.newFixedThreadPool(N_THREADS)
exec.execute({fileCrawler(queue, filter, roots)} as Runnable)
(1..N_MODULES).each {
exec.execute({printer(queue, exec)} as Runnable)
}
}
def conf = new Properties()
conf.load(getClass().getResourceAsStream("/conf.properties"))
// Starting Print to Word Document
def roots = []
conf['entitymodel.dir'].tokenize(',').each { path ->
roots << new File(path)
}
startPrinting roots
2008-12-12 18:7:11
Posted in
Tags:
Comments: 
