通過工具創建類型文件時通常會使用到各式各樣的模板,例如IDE在創建java文件時會自動在文件頭添加作者和創建日期的注釋、XML文件會自動添加根元素的標簽等。在NetBeans里通過使用文件模板可以很方便的按照既定的模式創建你的類型文件。
我在《創建新的文件類型
》里介紹了怎樣創建一個自定義的文件類型,創建后的文件類型里就包含了一個名為MapTemplate.xmap的空文件模板。現在我們將它變成一個真正有用的地圖文件模板,并且使用向導收集初始參數創建地圖文件。
創建模塊項目
現在創建一個模塊套件項目Florence
和一個模塊項目MapCoreUI
,將前文中創建的MapCore
模塊項目和MapCoreUI
一起加入Florence
。從命名就可以看出,我們的新建文件向導會在MapCoreUI
中實現。

創建文件模板
打開我們之前創建的MapCore
項目,在包org.jqueen.nb.map.core
下可以找到一個名為MapTemplate.xmap
的空文件模板,雙擊打開后我們給它添加內容使其變成一個真正有用的地圖文件模板:
<?xml version="1.0" encoding="UTF-8"?>
<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

下面分別是兩個類的全部源碼:
package org.jqueen.nb.map.core.ui.wizard;
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();
}
}
package org.jqueen.nb.map.core.ui.wizard;
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
,新增一個方法:
private String getFileName() {
return ((CreateMapVisualPanel1) getComponent()).getFileName();
}
然后在storeSettings()
方法里添加代碼:
((WizardDescriptor) settings).putProperty(CreateMapVisualPanel1.FILE_NAME, getFileName());
((TemplateWizard) settings).setTargetName(getFileName());
打開文件CreateMapWizardPanel2.java
,新增一組方法:
private int getMapWidth(){
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_WIDTH, getMapWidth());
((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()
方法:
FileObject dir = Templates.getTargetFolder(wizard);
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個補充模板腳本的方法:
private String getTileScript(int rows, int cols) {
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="Templates">
<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格式文件了。




