| 概念 | XML 標籤 | 角色 |
|---|---|---|
| 擴展點 | ` | 宣告插件提供的「插座」,供其他插件插入功能 |
| 擴展 | ` | 使用他人擴展點,「插頭」端,指定 `point` 屬性 |
| 插件 | `plugin.xml` | 功能單元,含擴展點宣告與擴展實現 |
| 插件註冊表 | 啟動時建立 | 內存中全域表,記錄所有擴展點與擴展關係 |
向**彈出選單**貢獻選單項,針對特定物件類型 (透過 `objectClass` 過濾)。
<extension point="org.eclipse.ui.popupMenus">
<objectContribution
objectClass="org.eclipse.jdt.core.IMember"
id="com.example.jdt.modifiers">
<menu
label="Soln: Modifiers"
path="group.reorganize"
id="com.example.jdt.modifiers.menu">
<separator name="group1"/>
<separator name="group2"/>
</menu>
<action
label="Private"
menubarPath="com.example.jdt.modifiers.menu/group1"
class="com.example.jdt.actions.MakePrivateAction"
id="com.example.jdt.modifiers.makePrivate"/>
<action
label="Protected"
menubarPath="com.example.jdt.modifiers.menu/group1"
class="com.example.jdt.actions.MakeProtectedAction"
id="com.example.jdt.modifiers.makeProtected"/>
<action
label="Public"
menubarPath="com.example.jdt.modifiers.menu/group1"
class="com.example.jdt.actions.MakePublicAction"
id="com.example.jdt.modifiers.makePublic"/>
<action
label="Package (Default)"
menubarPath="com.example.jdt.modifiers.menu/group1"
class="com.example.jdt.actions.MakePackageAction"
id="com.example.jdt.modifiers.makePackage"/>
<action
label="Toggle Final"
menubarPath="com.example.jdt.modifiers.menu/group2"
class="com.example.jdt.actions.ToggleFinalAction"
id="com.example.jdt.modifiers.toggleFinal"/>
</objectContribution>
</extension>
Action 類實作 `IObjectActionDelegate`,自動接收選中變更通知:
public abstract class AbstractModifierAction implements IObjectActionDelegate {
protected IMember[] selectedMembers = new IMember[0];
@Override
public void selectionChanged(IAction action, ISelection selection) {
if (selection instanceof IStructuredSelection) {
IStructuredSelection ss = (IStructuredSelection) selection;
List<IMember> members = new ArrayList<>();
for (Object obj : ss.toArray()) {
if (obj instanceof IMember) {
members.add((IMember) obj);
}
}
this.selectedMembers = members.toArray(new IMember[0]);
} else {
this.selectedMembers = new IMember[0];
}
// 更新選單項啟用狀態
action.setEnabled(canExecute());
}
protected boolean canExecute() {
return selectedMembers.length > 0;
}
@Override
public void run(IAction action) {
for (IMember member : selectedMembers) {
if (isApplicable(member)) {
modifyMember(member);
}
}
}
protected abstract boolean isApplicable(IMember member);
protected abstract void modifyMember(IMember member);
}
修改方法修飾符屬於**局部詞法修改**,不需完整 AST 解析。`IScanner` (詞法掃描器) 完美勝任:
public void modifyMember(IMember member) {
ICompilationUnit cu = member.getCompilationUnit();
if (!cu.isWorkingCopy()) return; // 僅支援編輯器中開啟的工作副本
try {
IBuffer buffer = cu.getBuffer();
ISourceRange range = member.getSourceRange(); // 方法在源碼中的範圍
// 建立掃描器,限制掃描範圍
IScanner scanner = ToolFactory.createScanner(false, false, false, false);
scanner.setSource(buffer.getCharacters());
scanner.resetTo(range.getOffset(), range.getOffset() + range.getLength() - 1);
// 掃描至左括號 '(' 為止 (方法簽名結束)
int token = scanner.getNextToken();
while (token != ITerminalSymbols.TokenNameEOF
&& token != ITerminalSymbols.TokenNameLPAREN) {
if (token == ITerminalSymbols.TokenNamePUBLIC) {
// 找到 public,替換為 private
buffer.replace(
scanner.getCurrentTokenStartPosition(),
scanner.getCurrentTokenEndPosition(),
"private"
);
break; // 方法只有一個可視性修飾符
}
// 同理處理 private、protected...
token = scanner.getNextToken();
}
cu.reconcile(ICompilationUnit.NO_AST, false, null, null); // 同步模型、刷新視圖
} catch (JavaModelException | BadLocationException e) {
// 錯誤處理
}
}
// 掃描過程中記錄 final 位置
int finalTokenPos = -1;
while (token != ITerminalSymbols.TokenNameEOF && token != ITerminalSymbols.TokenNameLPAREN) {
if (token == ITerminalSymbols.TokenNameFINAL) {
finalTokenPos = scanner.getCurrentTokenStartPosition();
}
token = scanner.getNextToken();
}
if (finalTokenPos >= 0) {
// 有 final -> 移除
buffer.replace(finalTokenPos, finalTokenPos + "final".length(), "");
} else {
// 無 final -> 在可視性修飾符後插入 (或開頭)
int insertPos = findInsertPosition(scanner, buffer);
buffer.replace(insertPos, insertPos, "final ");
}
// 抽象基類
public abstract class AbstractModifierAction implements IObjectActionDelegate { ... }
// 具體實現
public class MakePrivateAction extends AbstractModifierAction {
protected void modifyMember(IMember member) {
changeVisibility(member, Flags.AccPrivate);
}
}
public class MakeProtectedAction extends AbstractModifierAction {
protected void modifyMember(IMember member) {
changeVisibility(member, Flags.AccProtected);
}
}
public class MakePublicAction extends AbstractModifierAction {
protected void modifyMember(IMember member) {
changeVisibility(member, Flags.AccPublic);
}
}
public class MakePackageAction extends AbstractModifierAction {
protected void modifyMember(IMember member) {
changeVisibility(member, 0); // 移除所有可視性修飾符
}
}
public class ToggleFinalAction extends AbstractModifierAction {
protected void modifyMember(IMember member) {
toggleFinal(member);
}
}
<extension point="org.eclipse.ui.bindings">
<key sequence="Ctrl+Shift+M"
contextId="org.eclipse.jdt.ui.javaEditorScope"
commandId="com.example.jdt.modifiers.makePrivate"
schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"/>
</extension>
這個插件雖小,却完整展示了 **Eclipse JDT 擴展開發的核心技術棧**:擴展點機制、JDT Java 模型 (IMember, ICompilationUnit)、詞法掃描器 (IScanner)、編輯器緩衝區 (IBuffer)、模型協調 (reconcile)。掌握這套技術,你就能為 Eclipse 打造任何針對 Java 代碼的客製化重構、分析、生成工具——**幾十行代碼,改變日常開發體驗**。