XtextのEObjectのメモ。
|
Xtextで生成されたエディターで入力したDSLは、内部ではEObjectクラスのツリーとして保持される。
Xtextの外部からは、URIを通してEObjectを指し示すことが出来る。
(このURIクラスはjava.net.URIではなく、EMFのURIクラス)
import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil;
EObject object = 〜; URI uri1 = object.eResource().getURI(); // 例)platform:/resource/example/src/test.mydsl URI uri2 = EcoreUtil.getURI(object); // 例)platform:/resource/example/src/test.mydsl#/
※EObjectの内容によっては、eResource()がnullになることもあるので注意。
EObjectを表すURIは、「platform:/resource/example/src/test.mydsl
」や「platform:/resource/example/src/test.mydsl#//@greetings.0
」という形式をしている。
「platform:/resource/
」は固定で、その直後の「example」がプロジェクト名、それ以降がファイルのパスを表す。
「#
」以降(fragment)が付いていないものは、ファイルそのものを指す。
フラグメントが付いているものは、ファイル内の個々のEObjectを指す。
フラグメントは「/
」区切りでEObjectの階層を表している。
フラグメントが「#/
」のみだと、ファイルのトップにあるEObjectを指す。
フラグメントが「#//@greetings.0
」の例だと、トップのEObjectのgreetingsというリストの0番目のEObjectを指す。リストでない場合はピリオドを付けない(「@greeting
」の様になる)。
URIは以下の様にして作る。
import org.eclipse.emf.common.util.URI;
String projectName = "example"; String filePath = "src/test.mydsl"; URI uri = URI.createPlatformResourceURI(projectName +"/" + filePath, true);
IFile file = 〜; URI uri = URI.createPlatformResourceURI(file.getFullPath().toPortableString(), true);
IStorage(IFile)からURIに変換するクラスを使うことも出来る。
import org.eclipse.xtext.ui.resource.IStorage2UriMapper; import com.google.inject.Injector; import org.xtext.example.mydsl.ui.internal.MyDslActivator;
IFile file = 〜; Injector injector = MyDslActivator.getInstance().getInjector(MyDslActivator.ORG_XTEXT_EXAMPLE_MYDSL_MYDSL); IStorage2UriMapper mapper = injector.getInstance(IStorage2UriMapper.class); URI uri = mapper.getUri(file);
フラグメントは以下の様にして追加する。
URI uri = 〜; URI uri2 = uri.appendFragment("/"); URI uri3 = uri.appendFragment("//@greetings.0");
import java.util.List; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import com.google.inject.Injector; import org.xtext.example.mydsl.ui.internal.MyDslActivator;
public static EObject getEObject(URI uri) { Injector injector = MyDslActivator.getInstance().getInjector(MyDslActivator.ORG_XTEXT_EXAMPLE_MYDSL_MYDSL); ResourceSet resourceSet = injector.getInstance(ResourceSet.class); return resourceSet.getEObject(uri, true); }
resourceSet.getEObject()は、URIにフラグメントが無い状態だとNullPointerExceptionが発生する。 (Xtext 2.4.2)
0 [Worker-6] ERROR org.eclipse.xtext.linking.lazy.LazyLinkingResource - resolution of uriFragment 'null' failed. java.lang.NullPointerException at org.eclipse.xtext.linking.lazy.LazyURIEncoder.isCrossLinkFragment(LazyURIEncoder.java:195) at org.eclipse.xtext.linking.lazy.LazyLinkingResource.getEObject(LazyLinkingResource.java:173) at org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.getEObject(ResourceSetImpl.java:223) 〜
× platform:/resource/example/src/test.mydsl
○ platform:/resource/example/src/test.mydsl#/
public static List<EObject> getEObjectList(URI uri) { Injector injector = MyDslActivator.getInstance().getInjector(MyDslActivator.ORG_XTEXT_EXAMPLE_MYDSL_MYDSL); ResourceSet resourceSet = injector.getInstance(ResourceSet.class); Resource r = resourceSet.getResource(uri, true); return r.getContents(); }
resourceSet.getResource()は、URIにフラグメントが有っても無くてもリソース(ファイル)を返す。
したがって、Resource#getContents()はファイル内のトップのEObjectを返す。
resourceSet.getResource(uri, true)
で「プロローグにはコンテンツを指定できません(Content
is not allowed in prolog)」という例外が発生するときは、Xtextの初期化がされていないらしい。[2015-04-28]
Caused by: org.eclipse.emf.ecore.resource.impl.ResourceSetImpl$1DiagnosticWrappedException: org.xml.sax.SAXParseExceptionpublicId: platform:/resource/example/src/main/example.mydsl; systemId: platform:/resource/example/src/main/example.mydsl; lineNumber: 1; columnNumber: 1; プロローグにはコンテンツを指定できません。 at org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.handleDemandLoadException(ResourceSetImpl.java:319) at org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.demandLoadHelper(ResourceSetImpl.java:278) at org.eclipse.xtext.resource.XtextResourceSet.getResource(XtextResourceSet.java:201) at org.eclipse.xtext.resource.SynchronizedXtextResourceSet.getResource(SynchronizedXtextResourceSet.java:26) 〜
以下のように初期化処理を自分で呼び出せば大丈夫。
MyDslStandaloneSetup.doSetup(); Injector injector = MyDslActivator.getInstance().getInjector(MyDslActivator.ORG_XTEXT_EXAMPLE_MYDSL_MYDSL); 〜
参考: Eclipse Community ForumsのContent is not allowed in prolog.
しかし初期化処理が呼ばれないのは、初期化処理(plugin.xml等)の記述に誤りがある可能性が高い。[2015-05-03]
特に作成対象となる拡張子を後から変更した場合、Javaソース内やplugin.xmlの各所に拡張子の指定があるので、全部変える必要がある。
EObjectがプロキシーの場合は、中身が空っぽの場合がある。[2013-09-03]
EcoreUtil.resolve()で解決させることが出来る。
EObject object = 〜; if (object.eIsProxy()) { Injector injector = MyDslActivator.getInstance().getInjector(MyDslActivator.ORG_XTEXT_EXAMPLE_MYDSL_MYDSL); ResourceSet resourceSet = injector.getInstance(ResourceSet.class); object = EcoreUtil.resolve(model, resourceSet); }
IFileからEObjectを取得するには、基本的にはIFileからURIを生成して、URIからEObjectを取得する。
IFileのエディターが開いている場合はXtextDocumentUtilを使ってEObjectを取得できる。
import org.eclipse.xtext.ui.editor.model.IXtextDocument; import org.eclipse.xtext.ui.editor.model.XtextDocumentUtil; import org.eclipse.xtext.ui.editor.utils.EditorUtils;
IFile file = 〜; List<EObject> list = getEObject(file); XtextEditor editor = EditorUtils.getActiveXtextEditor(); List<EObject> list = getEObject(file);
private static List<EObject> getEObject(Object obj) { IXtextDocument doc = XtextDocumentUtil.get(obj); if (doc == null) { return null; } EList<EObject> list = doc.readOnly(new IUnitOfWork<EList<EObject>, XtextResource>() { @Override public EList<EObject> exec(XtextResource state) throws Exception { return state.getContents(); } }); return list; }
XtextDocumentUtilはuiプロジェクトで使用できる。
XtextDocumentUtil.get()にはIFileやXtextEditor等が渡せる。
ただし該当ファイルのエディターが開いていないとnullが返る。
IXtextDocument#readOnly()は、ドキュメント内のオブジェクトにアクセスするメソッド。
引数にはIUnitOfWorkというクラスを渡す。型引数は、1個目が戻り値の型、2個目はXtextResourceで固定。
IFileのエディターが開いている場合はXtextDocumentUtilでIXtextDocumentを取得できるが、開いていない場合は以下のようにして取得できる。[2013-09-01]
import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.xtext.resource.XtextResource; import org.eclipse.xtext.ui.editor.model.XtextDocument; import com.google.inject.Injector; import org.xtext.example.mydsl.ui.internal.MyDslActivator;
public static getXtextDocument(IFile file) { Injector injector = MyDslActivator.getInstance().getInjector(MyDslActivator.ORG_XTEXT_EXAMPLE_MYDSL_MYDSL); URI uri = URI.createPlatformResourceURI(file.getFullPath().toPortableString(), true); ResourceSet resourceSet = injector.getInstance(ResourceSet.class); Resource resource = resourceSet.getResource(uri, true); XtextDocument document = injector.getInstance(XtextDocument.class); document.setInput((XtextResource) resource); return document; }
参考: GamlUtils
イベントハンドラー内でXtextEditorを取得し、選択されているEObjectを取得する例。[2014-11-19]
import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.jface.text.ITextSelection; import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.resource.EObjectAtOffsetHelper; import org.eclipse.xtext.resource.XtextResource; import org.eclipse.xtext.ui.editor.XtextEditor; import org.eclipse.xtext.ui.editor.model.IXtextDocument; import org.eclipse.xtext.ui.editor.utils.EditorUtils; import org.eclipse.xtext.util.concurrent.IUnitOfWork; import com.google.inject.Inject;
public class ExampleHandler extends AbstractHandler { @Inject private EObjectAtOffsetHelper helper; // private EObjectAtOffsetHelper helper = new EObjectAtOffsetHelper();
// @Override public Object execute(ExecutionEvent event) throws ExecutionException { XtextEditor editor = (XtextEditor) EditorUtils.getActiveXtextEditor(event); if (editor == null) { return; } ITextSelection selection = (ITextSelection) editor.getSelectionProvider().getSelection(); final int offset = selection.getOffset(); IXtextDocument document = editor.getDocument(); EObject object = document.readOnly(new IUnitOfWork<EObject, XtextResource>() { // @Override public EObject exec(XtextResource state) throws Exception { return helper.resolveContainedElementAt(state, offset); } }); return null; }
参考: Java Code Examples for org.eclipse.xtext.ui.editor.XtextEditor
IProjectからIFile一覧を取得すれば、IFileからEObjectを取得することは出来るが。
IXtextEObjectSearchを使うと、ワークスペース全体のEObjectを取得することが出来る。
IXtextEObjectSearchをカスタマイズして該当プロジェクトのEObjectだけ抽出すればいい。
import java.util.ArrayList; import java.util.List; import org.eclipse.core.resources.IProject; import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.resource.IEObjectDescription; import org.eclipse.xtext.ui.search.IXtextEObjectSearch; import com.google.inject.Injector; import org.xtext.example.mydsl.ui.internal.MyDslActivator;
public static List<EObject> getEObjectList(IProject project) { List<EObject> list = new ArrayList<EObject>(); IXtextEObjectSearch search = new MySearch(project.getName()); Injector injector = MyDslActivator.getInstance().getInjector(MyDslActivator.ORG_XTEXT_EXAMPLE_MYDSL_MYDSL); injector.injectMembers(search); Iterable<IEObjectDescription> matches = search.findMatches("", ""); for (IEObjectDescription desc : matches) { EObject object = desc.getEObjectOrProxy(); list.add(object); } return list; }
findMatches()には、フィルター(名称での絞り込み)が指定できる。
第1引数がname要素の条件、第2引数はルールクラス名の条件となる。
import java.util.Collections; import org.eclipse.emf.common.util.URI; import org.eclipse.xtext.resource.IEObjectDescription; import org.eclipse.xtext.resource.IResourceDescription; import org.eclipse.xtext.ui.search.IXtextEObjectSearch; import com.google.common.base.Function; import com.google.common.collect.Iterables;
public class MySearch extends IXtextEObjectSearch.Default { private String projectName;
public MySearch(String projectName) { this.projectName = projectName; }
@Override protected Iterable<IEObjectDescription> getSearchScope() { return Iterables.concat(Iterables.transform(getResourceDescriptions().getAllResourceDescriptions(), new Function<IResourceDescription, Iterable<IEObjectDescription>>() { @Override public Iterable<IEObjectDescription> apply(IResourceDescription from) { URI uri = from.getURI(); if (isTarget(uri)) { return from.getExportedObjects(); } return Collections.emptyList(); } })); }
private boolean isTarget(URI uri) { if (uri.fileExtension().equals("mydsl")) { //ファイルの拡張子 String[] seg = uri.segments(); if (seg.length >= 2 && projectName.equals(seg[1])) { //プロジェクト名 return true; } } return false; } }
デフォルトのIXtextEObjectSearch(IXtextEObjectSearch.
Default)では、Xtextで作られた全DSLファイルが対象になる。
そこで、MySearchではプロジェクト名やファイルの拡張子で絞り込むようにしている。
EObjectの位置へジャンプする(エディターを開く)ことが出来る。
ジャンプ先はURIで指定する。
import org.eclipse.emf.common.util.URI; import org.eclipse.ui.IEditorPart; import org.eclipse.xtext.ui.editor.GlobalURIEditorOpener; import com.google.inject.Injector; import org.xtext.example.mydsl.ui.internal.MyDslActivator;
public static boolean jump(URI uri) { Injector injector = MyDslActivator.getInstance().getInjector(MyDslActivator.ORG_XTEXT_EXAMPLE_MYDSL_MYDSL); // GlobalURIEditorOpener opener = new GlobalURIEditorOpener(); // injector.injectMembers(opener); GlobalURIEditorOpener opener = injector.getInstance(GlobalURIEditorOpener.class); IEditorPart editor = opener.open(uri, true); return editor != null; }
(ファイル等とは無関係に)EObjectを新規に生成する方法。[2013-09-02]
import org.eclipse.emf.ecore.util.EcoreUtil; import org.xtext.example.mydsl.myDsl.Greeting; import org.xtext.example.mydsl.myDsl.Model; import org.xtext.example.mydsl.myDsl.MyDslPackage;
Greeting greeting = (Greeting) EcoreUtil.create(MyDslPackage.Literals.GREETING); Model model 〜; EList<Greeting> list = model.getGreetings(); list.add(greeting);
EObjectを複製する方法。[2013-09-02]
Greeting greeting = 〜; Greeting dup = EcoreUtil.copy(greeting);
親オブジェクトからEObjectを削除する方法。[2013-09-02]
Greeting greeting = 〜; EcoreUtil.remove(greeting);
greetingが属している親オブジェクトを探して、そこからgreetingを削除してくれる。
※EObjectはコメントも含んでいるので、EObjectを削除するとコメントも消えるので注意。