使用文件模板和向导创建一个类型文件
通过工具创建类型文件时通常会使用到各式各样的模板,例如IDE在创建java文件时会自动在文件头添加作者和创建日期的注释、XML文件会自动添加根元素的标签等。在NetBeans里通过使用文件模板可以很方便的按照既定的模式创建你的类型文件。
我在《创建新的文件类型 》里介绍了怎样创建一个自定义的文件类型,创建后的文件类型里就包含了一个名为MapTemplate.xmap的空文件模板。现在我们将它变成一个真正有用的地图文件模板,并且使用向导收集初始参数创建地图文件。
创建模块项目
现在创建一个模块套件项目Florence 和一个模块项目MapCoreUI ,将前文中创建的MapCore 模块项目和MapCoreUI 一起加入Florence 。从命名就可以看出,我们的新建文件向导会在MapCoreUI 中实现。
创建文件模板
打开我们之前创建的MapCore 项目,在包org.jqueen.nb.map.core 下可以找到一个名为MapTemplate.xmap 的空文件模板,双击打开后我们给它添加内容使其变成一个真正有用的地图文件模板:
< map version ="1.0" author ="${user}" >
< summary >
< attr name ="width" intvalue ="${mapWidth}" />
< attr name ="height" intvalue ="${mapHeight}" />
</ summary >
< resource size ="1" >
< iconresource >
< attr name ="ID" intvalue ="0" />
< attr name ="URL" stringvalue ="${iconresourceURL}" />
</ iconresource >
</ resource >
< layers size ="${layerSize}" >
< layer type ="TileLayer" >
< attr name ="name" stringvalue ="tile" />
< attr name ="depth" intvalue ="0" />
< attr name ="tileRows" intvalue ="${tileRows}" />
< attr name ="tileCols" intvalue ="${tileCols}" />
< attr name ="tileWidth" intvalue ="${tileWidth}" />
< attr name ="tileHeight" intvalue ="${tileHeight}" />
${tiles}
</ layer >
${thingLayers}
</ layers >
</ map >
添加完成后把这个文件复制到MapCoreUI 项目的org/jqueen/nb/map/core/ui 目录下。
创建新建文件向导
1、打开项目MapCoreUI ,点击新建文件菜单,在类别列表中选择“模块开发”,文件类型列表中选择“向导”,点击“下一步”操作。
2、注册类型选择“新建文件”,再在向导面板数输入框输入“2”,点击“下一步”操作。
3、类名前缀输入框输入“CreateMap”,
显示名称输入“Map File”,
类别选择“其他”,
包输入框输入“org.jqueen.nb.map.core.ui.wizard”,最后点击“完成”结束操作。
这时,我们可以看到,IDE自动为我们在项目文件夹里新建了许多文件,我们打开2个组件面板文件设计向导的界面:
CreateMapVisualPanel1.java
CreateMapVisualPanel2.java
下面分别是两个类的全部源码:
import javax.swing.JPanel;
import org.openide.loaders.DataFolder;
import org.openide.util.Utilities;
/** * * @author Leon Chen */
public final class CreateMapVisualPanel1 extends JPanel {
public static final String FILE_NAME = " fileName " ;
/** Creates new form
CreateMapVisualPanel1 */
public CreateMapVisualPanel1() {
initComponents();
}
@Override
public String getName() {
return " 名称设置 " ;
}
/** This method is called from within the constructor to * initialize
the form. * WARNING: Do NOT modify this code. The content of this
method is * always regenerated by the Form Editor. */
// <editor-fold defaultstate="collapsed" desc = "Generated Code" >
private void initComponents() {
labName = new javax.swing.JLabel();
labFiles = new javax.swing.JLabel();
txtName = new javax.swing.JTextField();
labMapFile = new javax.swing.JLabel();
labBlockFile = new javax.swing.JLabel();
org.openide.awt.Mnemonics.setLocalizedText(labName,
org.openide.util.NbBundle.getMessage(CreateMapVisualPanel1. class ,
" CreateMapVisualPanel1.labName.text " )); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(labFiles,
org.openide.util.NbBundle.getMessage(CreateMapVisualPanel1. class ,
" CreateMapVisualPanel1.labFiles.text " )); // NOI18N
txtName.setText(org.openide.util.NbBundle.getMessage(CreateMapVisualPanel1. class ,
" CreateMapVisualPanel1.txtName.text " )); // NOI18N
txtName.addKeyListener( new java.awt.event.KeyAdapter() {
public void keyReleased(java.awt.event.KeyEvent evt) {
txtNameKeyReleased(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(labMapFile,
org.openide.util.NbBundle.getMessage(CreateMapVisualPanel1. class ,
" CreateMapVisualPanel1.labMapFile.text " )); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(labBlockFile,
org.openide.util.NbBundle.getMessage(CreateMapVisualPanel1. class ,
" CreateMapVisualPanel1.labBlockFile.text " )); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout( this );
this .setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(layout.createSequentialGroup().addContainerGap().addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addComponent(labMapFile, javax.swing.GroupLayout.DEFAULT_SIZE, 380 ,
Short.MAX_VALUE).addGroup(layout.createSequentialGroup().addComponent(labName).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(txtName, javax.swing.GroupLayout.DEFAULT_SIZE, 316 ,
Short.MAX_VALUE)).addComponent(labFiles).addComponent(labBlockFile,
javax.swing.GroupLayout.DEFAULT_SIZE, 380 , Short.MAX_VALUE)).addContainerGap()));
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(layout.createSequentialGroup().addContainerGap().addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE).addComponent(labName).addComponent(txtName,
javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE)).addGap( 91 , 91 , 91 ).addComponent(labFiles).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(labMapFile).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(labBlockFile).addContainerGap( 151 , Short.MAX_VALUE)));
} // </editor-fold> private void
txtNameKeyReleased(java.awt.event.KeyEvent evt) {
String path = "" ;
DataFolder dataFolder = Utilities.actionsGlobalContext().lookup(DataFolder. class );
if (dataFolder != null ) {
path = dataFolder.getPrimaryFile().getPath();
}
String name = txtName.getText().trim();
labMapFile.setText(path + " / " + name + " .map " );
labBlockFile.setText(path + " / " + name + " .block " );
}
// Variables declaration - do not modify
private javax.swing.JLabel labBlockFile;
private javax.swing.JLabel labFiles;
private javax.swing.JLabel labMapFile;
private javax.swing.JLabel labName;
private javax.swing.JTextField txtName;
// End of variables declaration
public String getFileName() {
return txtName.getText().trim();
}
}
import javax.swing.JPanel;
/** * * @author Leon Chen */
public final class CreateMapVisualPanel2 extends JPanel {
public static final String MAP_WIDTH = " mapWidth " ;
public static final String MAP_HEIGHT = " mapHeight " ;
public static final String THING_LAYER_SIZE = " thingLayerSize " ;
public static final String TILE_WIDTH = " tileWidth " ;
public static final String TILE_HEIGHT = " tileHeight " ;
public static final String ICON_URL = " iconURL " ;
/** Creates new form
CreateMapVisualPanel2 */
public CreateMapVisualPanel2() {
initComponents();
}
@Override
public String getName() {
return " 参数设置 " ;
}
/** This method is called from within the constructor to * initialize
the form. * WARNING: Do NOT modify this code. The content of this
method is * always regenerated by the Form Editor. */
// <editor-fold defaultstate="collapsed" desc = "Generated Code" >
private void initComponents() {
labMapWidth = new javax.swing.JLabel();
jSpinner1 = new javax.swing.JSpinner();
labMapHeight = new javax.swing.JLabel();
jSpinner2 = new javax.swing.JSpinner();
labThingLayerSize = new javax.swing.JLabel();
jSpinner3 = new javax.swing.JSpinner();
labTileWidth = new javax.swing.JLabel();
jSpinner4 = new javax.swing.JSpinner();
labTileHeight = new javax.swing.JLabel();
jSpinner5 = new javax.swing.JSpinner();
labTileResource = new javax.swing.JLabel();
txtIcon = new javax.swing.JTextField();
btnOpen = new javax.swing.JButton();
org.openide.awt.Mnemonics.setLocalizedText(labMapWidth,
org.openide.util.NbBundle.getMessage(CreateMapVisualPanel2. class ,
" CreateMapVisualPanel2.labMapWidth.text " )); // NOI18N
jSpinner1.setModel( new javax.swing.SpinnerNumberModel( 960 , 0 , 9999 ,
64 ));
org.openide.awt.Mnemonics.setLocalizedText(labMapHeight,
org.openide.util.NbBundle.getMessage(CreateMapVisualPanel2. class ,
" CreateMapVisualPanel2.labMapHeight.text " )); // NOI18N
jSpinner2.setModel( new javax.swing.SpinnerNumberModel( 640 , 0 , 9999 ,
64 ));
org.openide.awt.Mnemonics.setLocalizedText(labThingLayerSize,
org.openide.util.NbBundle.getMessage(CreateMapVisualPanel2. class ,
" CreateMapVisualPanel2.labThingLayerSize.text " )); // NOI18N
jSpinner3.setModel( new javax.swing.SpinnerNumberModel( 3 , 1 , 10 , 1 ));
org.openide.awt.Mnemonics.setLocalizedText(labTileWidth,
org.openide.util.NbBundle.getMessage(CreateMapVisualPanel2. class ,
" CreateMapVisualPanel2.labTileWidth.text " )); // NOI18N
jSpinner4.setModel( new javax.swing.SpinnerNumberModel( 64 , 64 , 9999 ,
64 ));
org.openide.awt.Mnemonics.setLocalizedText(labTileHeight,
org.openide.util.NbBundle.getMessage(CreateMapVisualPanel2. class ,
" CreateMapVisualPanel2.labTileHeight.text " )); // NOI18N
jSpinner5.setModel( new javax.swing.SpinnerNumberModel( 64 , 64 , 9999 ,
64 ));
org.openide.awt.Mnemonics.setLocalizedText(labTileResource,
org.openide.util.NbBundle.getMessage(CreateMapVisualPanel2. class ,
" CreateMapVisualPanel2.labTileResource.text " )); // NOI18N
txtIcon.setEditable( false );
txtIcon.setText(org.openide.util.NbBundle.getMessage(CreateMapVisualPanel2. class ,
" CreateMapVisualPanel2.txtIcon.text " )); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(btnOpen,
org.openide.util.NbBundle.getMessage(CreateMapVisualPanel2. class ,
" CreateMapVisualPanel2.btnOpen.text " )); // NOI18N
btnOpen.addActionListener( new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnOpenActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout( this );
this .setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(layout.createSequentialGroup().addContainerGap().addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(layout.createSequentialGroup().addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(layout.createSequentialGroup().addComponent(labMapWidth).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE)).addGroup(layout.createSequentialGroup().addComponent(labMapHeight).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(jSpinner2, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE)).addGroup(layout.createSequentialGroup().addComponent(labThingLayerSize).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(jSpinner3, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE))).addGap( 255 , 255 , 255 )).addGroup(layout.createSequentialGroup().addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(layout.createSequentialGroup().addComponent(labTileWidth).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(jSpinner4, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE)).addGroup(layout.createSequentialGroup().addComponent(labTileHeight).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(jSpinner5, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE)).addGroup(layout.createSequentialGroup().addComponent(labTileResource).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(txtIcon, javax.swing.GroupLayout.DEFAULT_SIZE, 235 ,
Short.MAX_VALUE).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(btnOpen))).addContainerGap()))));
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(layout.createSequentialGroup().addContainerGap().addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE).addComponent(labMapWidth).addComponent(jSpinner1,
javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE)).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE).addComponent(labMapHeight).addComponent(jSpinner2,
javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE)).addGap( 18 , 18 , 18 ).addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE).addComponent(labThingLayerSize).addComponent(jSpinner3,
javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE)).addGap( 18 , 18 , 18 ).addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE).addComponent(labTileWidth).addComponent(jSpinner4,
javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE)).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE).addComponent(labTileHeight).addComponent(jSpinner5,
javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE)).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE).addComponent(labTileResource).addComponent(txtIcon,
javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE).addComponent(btnOpen)).addContainerGap( 103 , Short.MAX_VALUE)));
} // </editor-fold>
private void btnOpenActionPerformed(java.awt.event.ActionEvent evt) {
}
// Variables declaration - do not modify
private javax.swing.JButton btnOpen;
private javax.swing.JSpinner jSpinner1;
private javax.swing.JSpinner jSpinner2;
private javax.swing.JSpinner jSpinner3;
private javax.swing.JSpinner jSpinner4;
private javax.swing.JSpinner jSpinner5;
private javax.swing.JLabel labMapHeight;
private javax.swing.JLabel labMapWidth;
private javax.swing.JLabel labThingLayerSize;
private javax.swing.JLabel labTileHeight;
private javax.swing.JLabel labTileResource;
private javax.swing.JLabel labTileWidth;
private javax.swing.JTextField txtIcon;
// End of variables declaration
public int getMapWidth() {
return (Integer) jSpinner1.getValue();
}
public int getMapHeight() {
return (Integer) jSpinner2.getValue();
}
public int getThingLayerSize() {
return (Integer) jSpinner3.getValue();
}
public int getTileWidth() {
return (Integer) jSpinner4.getValue();
}
public int getTileHeight() {
return (Integer) jSpinner5.getValue();
}
public String getIconURL() {
return txtIcon.getText();
}
}
Ok,现在我们可以给组件关联的另外2个类添加获取输入参数的代码了。
打开文件CreateMapWizardPanel1.java ,新增一个方法:
return ((CreateMapVisualPanel1) getComponent()).getFileName();
}
然后在storeSettings() 方法里添加代码:
((TemplateWizard) settings).setTargetName(getFileName());
打开文件CreateMapWizardPanel2.java ,新增一组方法:
return ((CreateMapVisualPanel2) getComponent()).getMapWidth();
}
private int getMapHeight(){
return ((CreateMapVisualPanel2) getComponent()).getMapHeight();
}
private int getThingLayerSize(){
return ((CreateMapVisualPanel2) getComponent()).getThingLayerSize();
}
private int getTileWidth(){
return ((CreateMapVisualPanel2) getComponent()).getTileWidth();
}
private int getTileHeight(){
return ((CreateMapVisualPanel2) getComponent()).getTileHeight();
}
private String getIconURL(){
return ((CreateMapVisualPanel2) getComponent()).getIconURL();
}
然后在storeSettings() 方法里添加代码:
((WizardDescriptor) settings).putProperty(CreateMapVisualPanel2.MAP_HEIGHT, getMapHeight());
((WizardDescriptor) settings).putProperty(CreateMapVisualPanel2.THING_LAYER_SIZE, getThingLayerSize());
((WizardDescriptor) settings).putProperty(CreateMapVisualPanel2.TILE_WIDTH, getTileWidth());
((WizardDescriptor) settings).putProperty(CreateMapVisualPanel2.TILE_HEIGHT, getTileHeight());
((WizardDescriptor) settings).putProperty(CreateMapVisualPanel2.ICON_URL, getIconURL());
现在再重写CreateMapWizardIterator.java 的instantiate() 方法:
String targetName = Templates.getTargetName(wizard);
DataFolder df = DataFolder.findFolder(dir);
Integer mapWidth = (Integer) wizard.getProperty(CreateMapVisualPanel2.MAP_WIDTH);
Integer mapHeight = (Integer) wizard.getProperty(CreateMapVisualPanel2.MAP_HEIGHT);
String url = (String) wizard.getProperty(CreateMapVisualPanel2.ICON_URL);
Integer thingLayerSize = (Integer) wizard.getProperty(CreateMapVisualPanel2.THING_LAYER_SIZE);
Integer tileWidth = (Integer) wizard.getProperty(CreateMapVisualPanel2.TILE_WIDTH);
Integer tileHeight = (Integer) wizard.getProperty(CreateMapVisualPanel2.TILE_HEIGHT);
Integer tileRows = mapWidth / tileWidth;
if (mapWidth % tileWidth != 0 ) {
tileRows ++ ;
}
Integer tileCols = mapHeight / tileHeight;
if (mapHeight % tileHeight != 0 ) {
tileCols ++ ;
}
HashMap hashMap = new HashMap();
hashMap.put( " mapWidth " , mapWidth);
hashMap.put( " mapHeight " , mapHeight);
hashMap.put( " iconresourceURL " , url == null ? "" : url);
hashMap.put( " layerSize " , thingLayerSize + 1 );
hashMap.put( " tileRows " , tileRows);
hashMap.put( " tileCols " , tileCols);
hashMap.put( " tileWidth " , tileWidth);
hashMap.put( " tileHeight " , tileHeight);
hashMap.put( " tiles " , getTileScript(tileRows, tileCols));
hashMap.put( " thingLayers " , getThingLayerScript(thingLayerSize));
FileObject template = Templates.getTemplate(wizard);
DataObject dTemplate = DataObject.find(template);
DataObject dobj = dTemplate.createFromTemplate(df, targetName, hashMap);
FileObject createdFile = dobj.getPrimaryFile();
return Collections.singleton(createdFile);
我们再为这个类添加2个补充模板脚本的方法:
String script = "" ;
for ( int row = 0 ; row < rows; row ++ ) {
for ( int col = 0 ; col < cols; col ++ ) {
script += "" n < tile > " n " +
" <attr name= "" rowIndex "" intvalue= """ + row + """ /> " n " +
" <attr name= "" colIndex "" intvalue= """ + col + """ /> " n " +
" <attr name= "" iconResource "" intvalue= "" 0 "" /> " n " +
" </tile> " ;
}
}
script = script.replaceFirst( "" n " , "" );
return script;
}
private String getThingLayerScript( int size) {
String script = "" ;
for ( int i = 0 ; i < size; i ++ ) {
if (i > 0 ) {
script += "" ;
}
script += "" n < layer type = "" ThingLayer "" > " n " +
" <attr name= "" name "" stringvalue= "" ThingLayer " + i + """ /> " n " +
" <attr name= "" depth "" intvalue= """ + (i + 1 ) + """ /> " n " +
" </layer> " ;
}
script = script.replaceFirst( "" n " , "" );
return script;
}
当java类文件全部编写完成后,我们还需要来做一些配置的工作。
修改配置文件
打开项目的layer.xml 文件,我们可以看到已经自动生成了一些配置,我们对其做一些修改,将file 节点的name 属性更改为“MapTemplate.xmap ”,同时添加一个属性url="MapTemplate.xmap" ,用于和MapCore 模块中定义的文件类型同步以及指定文件模板。另外在file 节点下新增一条属性:“<attr name="javax.script.ScriptEngine" stringvalue="freemarker"/> ”。
< folder name ="Other" >
< file name ="MapTemplate.xmap" url ="MapTemplate.xmap" >
< attr name ="SystemFileSystem.localizingBundle" stringvalue ="org.jqueen.nb.map.core.ui.Bundle" />
< attr name ="instantiatingIterator" newvalue ="org.jqueen.nb.map.core.ui.wizard.CreateMapWizardIterator" />
< attr name ="template" boolvalue ="true" />
< attr name ="templateWizardURL" urlvalue ="nbresloc:/org/jqueen/nb/map/core/ui/wizard/createMap.html" />
< attr name ="javax.script.ScriptEngine" stringvalue ="freemarker" />
</ file >
</ folder >
</ folder >
打开同级目录下的Bundle.properties 文件,将Templates/Other/createMap=Map File 改为Templates/Other/MapTemplate.xmap=地图文件 。
最后,在项目的“重要文件”文件夹里双击“模块清单”文件,在最后一行添加代码:
OpenIDE-Module-Needs: javax.script.ScriptEngine.freemarker 。
现在,我们终于完成了所有的工作,编译安装我们的模块套件项目后就可以使用向导的方式创建xmap格式文件了。