Eclipse Modeling Framework (EMF) 將模型(UML、XSD 等)轉換為健壯的 Java 程式碼,並提供**模型驗證框架**確保資料符合自訂約束。本文介紹 EMF Validation Framework 核心概念、兩種驗證模式、約束實作與綁定流程,並以 Library 模型為例完整示範從批次驗證到即時驗證的遷移。
| 概念 | 說明 |
|---|---|
| 約束 | 實作 IModelConstraint,含 validate(IValidationContext) 邏輯,透過描述符定義模式、目標類型、嚴重度等。 |
| 驗證上下文 | IValidationContext 提供目標物件、事件類型、新舊值,並支援 skipCurrentConstraintFor()、快取機制提升效能。 |
| 驗證模式 | Batch(批次):用戶觸發,驗證整個模型集合;Live(即時):屬性變更通知觸發,驗證單一變更。 |
| 約束綁定 | 透過 org.eclipse.emf.validation.constraintBindings 擴展點,將 ClientContext(含選擇器)與約束類別關聯。 |
Library、Writer、Book 三個類別,含 name/title 屬性;BookCategory 列舉。
public class NonEmptyNamesConstraint extends AbstractModelConstraint {
public IStatus validate(IValidationContext ctx) {
EObject eObj = ctx.getTarget();
EMFEventType eType = ctx.getEventType();
// Batch 模式事件類型為 NULL
if (eType == EMFEventType.NULL) {
String name = null;
if (eObj instanceof Writer) name = ((Writer)eObj).getName();
else if (eObj instanceof Library) name = ((Library)eObj).getName();
else if (eObj instanceof Book) name = ((Book)eObj).getTitle();
if (name == null || name.length() == 0)
return ctx.createFailureStatus(new Object[]{eObj.eClass().getName()});
}
return ctx.createSuccessStatus();
}
}
<extension point="org.eclipse.emf.validation.constraintProviders">
<category name="Library Constraints" id="org.eclipse.emf.validation.example.library"/>
<constraintProvider cache="true">
<package namespaceUri="platform:/resource/test.emf.validation/model/Library.ecore"/>
<constraints categories="emf.validation.example.library">
<constraint lang="Java"
class="emf.model.library.validation.constraints.NonEmptyNamesConstraint"
severity="ERROR" mode="Batch" name="Non-Empty Names"
id="emf.validation.example.library.NameNotEmpty" statusCode="1">
<description>All items in a library model should have some unique identifier or name.</description>
<message>The required feature 'name' of '{}' must be set.</message>
<target class="Library"/>
<target class="Writer"/>
<target class="Book"/>
</constraint>
</constraints>
</constraintProvider>
</extension>
<extension point="org.eclipse.emf.validation.constraintBindings">
<clientContext default="false" id="emf.validation.example.libraryContext">
<selector class="emf.model.library.validation.constraints.ValidationDelegateClientSelector"/>
</clientContext>
<binding context="emf.validation.example.libraryContext" category="emf.validation.example.library"/>
</extension>
IBatchValidator validator = (IBatchValidator) ModelValidationService.getInstance()
.newValidator(EvaluationMode.BATCH);
validator.setIncludeLiveConstraints(true);
IStatus status = validator.validate(selectedEObjects);
// Live 模式:事件類型不為 NULL,透過 getFeatureNewValue() 取得新值
} else {
Object newValue = ctx.getFeatureNewValue();
if (newValue == null || ((String)newValue).length() == 0)
return ctx.createFailureStatus(new Object[]{eObj.eClass().getName()});
}
<target class="Library">
<event name="Set"><feature name="name"/></event>
<event name="Unset"><feature name="name"/></event>
</target>
<target class="Writer">...</target>
<target class="Book">
<event name="Set"><feature name="title"/></event>
<event name="Unset"><feature name="title"/></event>
</target>
並將 mode="Batch" 改為 mode="Live"。
Resource r = (Resource)i.next();
if (!resourceHasAdapter(r)) {
EContentAdapter liveValidationContentAdapter = new LiveValidationContentAdapter();
r.eAdapters().add(liveValidationContentAdapter);
}
EMF Validation Framework 提供強大且彈性的模型驗證機制,支援 Java/OCL 實作、批次/即時雙模式、自訂掃描演算法。善用此框架,能在模型層面攔截資料錯誤,大幅提升應用程式健壯度。