Być może, od dawna nie wymaga szczególnej prezentacji. Wiele osób zna Eclipse dzięki narzędziom do tworzenia aplikacji w Javie Eclipse (). To właśnie ta popularna otwarta IDE Java jest kojarzona przez większość programistów ze słowem „Eclipse”. Jednak Eclipse to również rozszerzalna platforma do integracji narzędzi programistycznych (Eclipse Platform) oraz szereg IDE zbudowanych na jej bazie, w tym JDT. Eclipse to także projekt Eclipse (Eclipse Project), projekt najwyższego poziomu, który koordynuje rozwój Eclipse Platform i JDT, oraz Eclipse SDK – wynik tych prac. Na koniec, Eclipse to Fundacja open-source z ogromną społecznością projektów, z których nie wszystkie są napisane w Javie lub mają związek z narzędziami programistycznymi (np. projekty i ). Świat Eclipse jest bardzo różnorodny.
W tym artykule, mającym charakter przeglądowy, spróbujemy przyjrzeć się niektórym podstawom architektury Eclipse jako platformy do tworzenia zintegrowanych narzędzi programistycznych oraz przedstawić wstępne informacje na temat komponentów Eclipse, które stanowią fundament technologicznej platformy dla „nowego Konfiguratora” 1C: Enterprise, . Oczywiście, takie rozważania będą w dużej mierze powierzchowne i dość ograniczone, również dlatego, że nie koncentrujemy się tylko na programistach Eclipse jako publiczności docelowej. Mamy nadzieję, że nawet doświadczeni programiści Eclipse znajdą w artykule interesujące dla siebie informacje. Na przykład, opowiemy o jednym z „sekretów Eclipse”, stosunkowo nowym i jeszcze mało znanym projekcie , który został założony i wspierany przez firmę 1C.

Wprowadzenie do architektury Eclipse
Rozpocznijmy od omówienia niektórych ogólnych aspektów architektury Eclipse na przykładzie (JDT). Wybór akurat JDT jako przykładu nie jest przypadkowy. To pierwsze zintegrowane środowisko programistyczne, które pojawiło się w Eclipse. Pozostałe projekty *DT Eclipse, takie jak Eclipse C/C++ Development Tooling (CDT), zostały stworzone później i zaczerpnęły zarówno podstawowe zasady architektoniczne, jak i niektóre fragmenty kodu źródłowego z JDT. Podstawy architektury, które zostały wprowadzone w JDT, są aktualne do dziś dla praktycznie każdej IDE zbudowanej na platformie Eclipse, w tym dla 1C:Enterprise Development Tools.
Przede wszystkim należy zauważyć, że Eclipse charakteryzuje się dość wyraźnym podziałem architektonicznym, oddzielając funkcjonalność niezależną od języka od funkcjonalności przeznaczonej do wsparcia konkretnych języków programowania oraz separując niezależne od UI komponenty „rdzeniowe” (core) od komponentów związanych z wsparciem interfejsu użytkownika.
Eclipse Platform definiuje ogólną, niezależną od języka infrastrukturę, a narzędzia do programowania w Javie dodają do Eclipse w pełni funkcjonalne środowisko IDE dla Javy. Zarówno Eclipse Platform, jak i JDT składają się z kilku komponentów, z których każda należy albo do niezależnego od UI „rdzenia”, albo do warstwy UI (rys. 1).

Rys. 1. Eclipse Platform i JDT
Wymieńmy główne komponenty Eclipse Platform:
- Runtime — Określa infrastrukturę wtyczek. Dla Eclipse charakterystyczna jest architektura modułowa. W istocie Eclipse jest kolekcją „punktów rozszerzenia” i „rozszerzeń”.
- Workspace — Zarządza jednym lub więcej projektami. Projekt składa się z folderów i plików, które są wyświetlane bezpośrednio w systemie plików.
- Standard Widget Toolkit (SWT) — Zapewnia podstawowe elementy interfejsu użytkownika, zintegrowane z systemem operacyjnym.
- JFace — Oferuje zestaw frameworków UI, zbudowanych na podstawie SWT.
- Workbench — Określa paradygmat UI Eclipse: edytory, widoki, perspektywy.
Należy zauważyć, że Eclipse Platform oferuje również wiele innych użytecznych komponentów do budowy zintegrowanych narzędzi programistycznych, wśród których można wymienić Debug, Compare, Search oraz Team. Oddzielnie należy wspomnieć o JFace Text – podstawie do budowy „inteligentnych edytorów” kodu źródłowego. Niestety, nawet pobieżne omówienie tych komponentów, jak również komponentów warstwy UI, nie jest możliwe w ramach tego artykułu, dlatego w pozostałej części tego rozdziału ograniczymy się do przeglądu głównych komponentów „rdzeniowych” Eclipse Platform i JDT.
Core Runtime
Infrastruktura wtyczek Eclipse opiera się na i jest udostępniana przez projekt Każda wtyczka Eclipse jest bundlem OSGi. Specyfikacja OSGi definiuje, w szczególności, mechanizmy wersjonowania i rozwiązywania zależności. Oprócz tych standardowych mechanizmów, Equinox wprowadza pojęcie punktu rozszerzenia.. Każdy plugin może definiować swoje punkty rozszerzeń oraz wprowadzać do systemu dodatkową funkcjonalność („rozszerzenia”), korzystając z punktów rozszerzeń zdefiniowanych przez ten sam lub inne pluginy. Szczegółowe omówienie mechanizmów OSGi i Equinox wykracza poza temat tego artykułu. Warto tylko zaznaczyć, że modularność w Eclipse ma totalny charakter (każda podsystema, łącznie z Runtime, składa się z jednego lub kilku pluginów), a praktycznie wszystko w Eclipse jest rozszerzeniem. Przy czym zasady te zostały wbudowane w architekturę Eclipse na długo przed wprowadzeniem OSGi (wówczas stosowano własną technologię, która w wielu aspektach przypominała OSGi).
Główna przestrzeń robocza
Praktycznie każda zintegrowana środowisko programistyczne zbudowane na podstawie Eclipse Platform działa z Eclipse workspace. To właśnie workspace zazwyczaj zawiera kod źródłowy rozwijanego w IDE aplikacji. Workspace jest bezpośrednio odwzorowany na system plików i składa się z projektów, które zawierają foldery i pliki. Te projekty, foldery i pliki nazywane są zasobami workspace. Implementacja workspace w Eclipse działa jak gdyby jako pamięć podręczna względem systemu plików, co pozwala na znaczne przyspieszenie przeszukiwania drzewa zasobów. Co więcej, workspace oferuje szereg dodatkowych usług, w tym i .
Za wsparcie workspace i jego zasobów odpowiada komponent Core Resources (plugin org.eclipse.core.resources). W szczególności ten komponent zapewnia programowy dostęp do workspace w postaci modelu zasobów. Aby skutecznie pracować z tym modelem, klientom potrzebny jest prosty sposób na reprezentowanie odniesienia do zasobu. Przy tym obiekt, który bezpośrednio przechowuje stan zasobu w modelu, powinien być ukryty przed dostępem klienta. W przeciwnym razie, w przypadku na przykład usunięcia pliku, klient mógłby nadal przechowywać obiekt, który już nie istnieje w modelu, co rodziłoby związane z tym problemy. Eclipse rozwiązuje ten problem, używając tzw. uchwytu zasobu. Uchwyt działa jako klucz (zna jedynie ścieżkę do zasobu w workspace) i całkowicie kontroluje dostęp do wewnętrznego obiektu modelu, który bezpośrednio przechowuje informacje o stanie zasobu. Ten projekt jest wariacją wzorca .
Rys. 2 ilustruje idiomę Handle/Body w kontekście modelu zasobów. Interfejs IResource reprezentuje uchwyt zasobu i jest API, w przeciwieństwie do klasy Resource, która implementuje ten interfejs, oraz klasy ResourceInfo, reprezentującej body, które API nie są. Należy podkreślić, że uchwyt zna tylko ścieżkę do zasobu względem korzenia workspace i nie zawiera odniesienia do resource info. Obiekty resource info tworzą tzw. „drzewo elementów” (element tree). Ta struktura danych jest w pełni zmaterializowana w pamięci. Aby znaleźć instancję resource info odpowiadającą pewnemu uchwytowi, drzewo elementów jest przeszukiwane zgodnie ze ścieżką przechowywaną w tym uchwycie.

Rys. 2. IResource i ResourceInfo
Jak zobaczymy w dalszej części, podstawowy projekt modelu zasobów (można go nazwać opartym na uchwycie) jest używany w Eclipse oraz w innych modelach. A na razie wymieńmy niektóre wyróżniające cechy tego projektu:
- Uchwyt jest obiektem-wartością (value object). Obiekty-wartości to obiekty niemutowalne (immutable), których równość nie opiera się na tożsamości. Takie obiekty mogą być bezpiecznie używane jako klucz w pojemnikach haszowanych. Kilka instancji uchwytu może odnosić się do tego samego zasobu. Do ich porównania należy użyć metody equals(Object).
- Uchwyt definiuje zachowanie zasobu, ale nie zawiera informacji o stanie zasobu (jedynymi danymi, które przechowuje, są „klucz”, ścieżka do zasobu).
- Uchwyt może odnosić się do nieistniejącego zasobu (lub zasobu, który jeszcze nie został utworzony, lub zasobu, który został już usunięty). Istnienie zasobu można sprawdzić za pomocą metody IResource.exists().
- Niektóre operacje mogą być realizowane wyłącznie na podstawie informacji przechowywanych w samym uchwycie (tzw. operacje handle-only). Przykładami są IResource.getParent(), getFullPath() itd. Zasób nie musi istnieć, aby taka operacja zakończyła się pomyślnie. Operacje, do pomyślnego wykonania których wymagane jest istnienie zasobu, zgłaszają wyjątek (CoreException), jeśli zasób nie istnieje.
Eclipse zapewnia skuteczny mechanizm powiadamiania o zmianach zasobów w workspace (rys. 3). Zasoby mogą być modyfikowane zarówno w wyniku działań wykonywanych w samej Eclipse IDE, jak i w wyniku synchronizacji z systemem plików. W obu przypadkach subskrybenci powiadomień otrzymują szczegółowe informacje o zmianach w postaci „delt zasobów” (resource delta). Delta opisuje zmiany między dwoma stanami (pod-)drzewa zasobów workspace i sama jest drzewem, w którym każdy węzeł opisuje zmianę określonego zasobu oraz zawiera listę delt następnego poziomu, opisujących zmiany zasobów podrzędnych.

Rys. 3. IResourceChangeEvent i IResourceDelta
Mechanizm powiadamiania oparty na deltach zasobów ma następujące cechy:
- Pojedyncza zmiana i wiele zmian są opisywane za pomocą tej samej struktury, ponieważ delta jest budowana na zasadzie rekursywnej kompozycji. Klienci subskrybujący mogą przetwarzać powiadomienia o zmianach zasobów poprzez rekursywne przechodzenie po drzewie delt.
- Delta zawiera pełne informacje o zmianie zasobu, w tym jego przenoszenie i/lub zmianę związanych z nim „znaczników” (np. błędów kompilacji przedstawianych w formie znaczników).
- Ponieważ odwołania do zasobu odbywają się przez handle, delta może naturalnie odwoływać się do usuniętego zasobu.
Jak wkrótce zobaczymy, podstawowe elementy projektowania mechanizmu powiadamiania o zmianach modeli zasobów są aktualne również dla innych modeli opartych na handle.
JDT Core
Model zasobów Eclipse workspace jest fundamentalnym modelem niezależnym od języka. Komponent JDT Core (wtyczka org.eclipse.jdt.core) udostępnia API do nawigacji i analizy struktury workspace z perspektywy Javy, tzw. „model Java” (Java model). To API jest definiowane w terminach elementów Javy, w przeciwieństwie do leżącego u podstaw API modelu zasobów, które jest definiowane w terminach folderów i plików. Podstawowe interfejsy drzewa elementów Javy są przedstawione na rys. 4.

Rys. 4. Elementy modelu Java
Model Java wykorzystuje tę samą idiom handle/body, co model zasobów (rys. 5). IJavaElement jest uchwytem, a JavaElementInfo odgrywa rolę body. Interfejs IJavaElement definiuje protokół wspólny dla wszystkich elementów Java. Niektóre z jego metod są tylko handle'em: getElementName(), getParent() itp. Obiekt JavaElementInfo przechowuje stan odpowiadającego mu elementu: jego strukturę i atrybuty.

Rys. 5. IJavaElement i JavaElementInfo
Model Java ma pewne różnice w implementacji podstawowego wzoru handle/body w porównaniu z modelem zasobów. Jak wspomniano wcześniej, w modelu zasobów drzewo elementów, którego węzłami są obiekty informacji o zasobach, jest całkowicie przechowywane w pamięci. Jednak w modelu Java może być znacznie więcej elementów niż w drzewie zasobów, ponieważ przedstawia on również wewnętrzną strukturę plików .java i .class: typy, pola i metody.
Aby uniknąć całkowitego zmaterializowania całego drzewa elementów w pamięci, implementacja modelu Java używa ograniczonego rozmiaru LRU-cache dla informacji o elementach, gdzie kluczem jest uchwyt IJavaElement. Obiekty informacji o elementach są tworzone na żądanie w miarę nawigacji w drzewie elementów. Przy tym najmniej często używane elementy są usuwane z cache, a zużycie pamięci przez model pozostaje ograniczone do zadanego rozmiaru cache. To kolejna zaleta wzoru opartego na uchwytach, który całkowicie ukrywa takie szczegóły implementacyjne przed kodem klienckim.
Mechanizm powiadamiania o zmianach elementów Java jest ogólnie podobny do omawianego wcześniej mechanizmu śledzenia zmian zasobów workspace. Klient, który chce śledzić zmiany w modelu Java, subskrybuje powiadomienia, które przedstawiane są jako obiekt ElementChangedEvent, który zawiera IJavaElementDelta (rys. 6).

Rys. 6. ElementChangedEvent i IJavaElementDelta
Model Java nie zawiera informacji o ciele metod ani o rozwiązywaniu nazw, dlatego do szczegółowej analizy kodu napisanego w Java, JDT Core dostarcza dodatkowy (nie oparty na uchwycie) model: (abstrakcyjna struktura składniowa, AST). AST przedstawia wynik analizy składniowej tekstu źródłowego. Węzły AST odpowiadają elementom struktury modułu źródłowego (deklaracjom, operatorom, wyrażeniom itp.) i zawierają informacje o współrzędnych odpowiadającego elementu w tekście źródłowym, a także (opcjonalnie) informacje o rozwiązywaniu nazw w postaci odwołań do tzw. bindingsBindings to obiekty, które reprezentują nazwane byty, takie jak typy, metody i zmienne znane kompilatorowi. W przeciwieństwie do węzłów AST, tworzących drzewo, bindings wspierają odniesienia krzyżowe i generalnie tworzą graf. Abstrakcyjna klasa ASTNode jest ogólną klasą bazową dla wszystkich węzłów AST. Podklasy ASTNode odpowiadają pewnym konstrukcjom składniowym języka Java.
Ponieważ drzewa składniowe mogą zużywać znaczną ilość pamięci, JDT buforuje tylko jedno AST dla aktywnego edytora. W przeciwieństwie do modelu Java, AST jest zazwyczaj traktowane jako "pośredni", "tymczasowy" model, o elementach którego klienci nie powinni trzymać odniesień poza kontekstem operacji, która doprowadziła do stworzenia AST.
Wymienione trzy modele (model Java, AST, bindings) wspólnie stanowią podstawę dla budowy „inteligentnych narzędzi programistycznych” w JDT, w tym potężnego edytora Java z różnorodnymi „asystentami”, różnymi czynnościami przetwarzania kodu źródłowego (w tym organizacją listy importu nazw oraz formatowaniem zgodnie z wyznaczonym stylem), narzędziami do przeszukiwania i refaktoryzacji. Model Java odgrywa szczególną rolę, ponieważ jest używany jako podstawa do wizualizacji struktury rozwijanego aplikacji (np. w Package Explorer, Outline, Search, Call Hierarchy i Type Hierarchy).
Komponenty Eclipse używane w 1С:Enterprise Developments Tools
Na rys. 7 przedstawiono komponenty Eclipse, które stanowią fundament technologicznej platformy dla 1C:Enterprise Development Tools.

Rys. 7. Eclipse jako platforma dla 1С:Enterprise Development Tools
Platforma Eclipse zapewnia podstawową infrastrukturę. Omówiliśmy niektóre aspekty tej infrastruktury w poprzedniej sekcji.
(EMF) zapewnia ogólne narzędzia do modelowania danych strukturalnych. EMF jest zintegrowany z platformą Eclipse, ale można go również używać oddzielnie, w standardowych aplikacjach Java. Bardzo często początkujący programiści Eclipse są już dobrze zaznajomieni z EMF, choć jeszcze nie do końca rozumieją subtelności platformy Eclipse. Jednym z powodów tak zasłużonej popularności jest uniwersalny projekt, który obejmuje również zunifikowane API na poziomie meta, umożliwiające ogólne operacje na dowolnym modelu EMF. Dostarczane przez EMF podstawowe implementacje dla obiektów modelu oraz podsystem generowania kodu modelu na podstawie meta-modelu znacznie zwiększają szybkość rozwoju i zmniejszają liczbę błędów. EMF zawiera również mechanizmy serializacji modeli, śledzenia zmian w modelu i wiele innych.
Jak każdy prawdziwie uniwersalny instrument, EMF nadaje się do rozwiązywania szerokiego zakresu zadań związanych z modelowaniem, ale niektóre klasy modeli (na przykład omówione powyżej modele oparte na uchwytach) mogą potrzebować bardziej wyspecjalizowanych narzędzi modelowania. Opowiadanie o EMF jest zajęciem niewdzięcznym, szczególnie w ograniczonych ramach jednego artykułu, ponieważ jest to temat osobnej książki, dość obszernej. Zauważmy tylko, że jakość systemu uogólnień, na którym opiera się EMF, pozwoliła na powstanie całego zestawu projektów poświęconych modelowaniu, które są częścią projektu wyższego poziomu. obok samego EMF. Jednym z takich projektów jest Eclipse Xtext.
zapewnia infrastrukturę do „modelowania tekstowego”. Xtext wykorzystuje do analizy składniowej oryginalnego tekstu oraz EMF do prezentacji wynikowego ASG (abstract semantic graph, który w zasadzie jest kombinacją AST i wiązań), zwanym również „modelem semantycznym”. Gramatyka modelowanego języka w Xtext jest opisana w jego własnym języku. Umożliwia to nie tylko generowanie opisu gramatyki dla ANTLR, ale także uzyskanie mechanizmu serializacji AST (tj. Xtext zapewnia zarówno parser, jak i unparser), kontekstowych podpowiedzi oraz szereg innych komponentów językowych. Z drugiej strony, język opisu gramatyki używany w Xtext jest mniej elastyczny w porównaniu, powiedzmy, do języka opisu gramatyki w ANTLR. Dlatego czasami trzeba „dostosować” realizowany język do Xtext, co zazwyczaj nie jest problemem, jeśli chodzi o język rozwijany od zera, ale może być nieakceptowalne dla języków o już ustalonym składzie. Mimo to, Xtext jest obecnie najbardziej dojrzałym, funkcjonalnie kompletnym i uniwersalnym narzędziem w Eclipse do budowy języków programowania i ich narzędzi deweloperskich. W szczególności jest to idealne narzędzie do szybkiego prototypowania. (domain-specific language, DSL). Oprócz wspomnianego wyżej „jądra językowego” opartego na ANTLR i EMF, Xtext zapewnia wiele użytecznych komponentów wyższego poziomu, w tym mechanizmy indeksowania, inkrementalnego budowania, „inteligentny edytor” i wiele, wiele więcej, ale pomija modeli językowych opartych na uchwytach. Podobnie jak EMF, Xtext jest tematem zasługującym na osobną książkę, i trudno nam będzie nawet pobieżnie opowiedzieć teraz o wszystkich jego możliwościach.
1С:Enterprise Development Tools aktywnie wykorzystuje zarówno EMF jako taki, jak i szereg innych projektów Eclipse Modeling. W szczególności, Xtext jest jedną z podstaw narzędzi deweloperskich dla takich języków jak 1С:Предприятие, w tym wbudowany język programowania i język zapytań. Inną podstawą tych narzędzi deweloperskich jest projekt Eclipse Handly, o którym szczegółowo opowiemy później (spośród wymienionych komponentów Eclipse jest on na razie najmniej znany).
, projekt podrzędny projektu głównego Eclipse Technology powstał w wyniku inicjalnej kontrybucji kodu do Eclipse Foundation, przeprowadzonej przez firmę 1C w 2014 roku. Od tego czasu firma 1C nadal wspiera rozwój projektu: committerzy Handly są pracownikami firmy. Projekt jest niewielki, ale zajmuje dość unikalną niszę w Eclipse: jego głównym celem jest wsparcie rozwoju modeli opartych na uchwytach.
Podstawowe zasady architektoniczne modeli opartych na uchwytach, takie jak idiom uchwyt/ciało, były omawiane powyżej na przykładzie modelu zasobów i modelu Java. Zauważono tam również, że zarówno model zasobów, jak i model Java stanowią ważną podstawę dla narzędzi do rozwoju oprogramowania Java (JDT) w Eclipse. A ponieważ praktycznie wszystkie projekty *DT Eclipse mają architekturę podobną do JDT, nie będzie dużym przesadzeniem powiedzieć, że modele oparte na uchwytach leżą u podstaw wielu, jeśli nie wszystkich IDE zbudowanych na platformie Eclipse. Na przykład w Eclipse C/C++ Development Tooling (CDT) istnieje model oparty na uchwytach C/C++, który odgrywa w architekturze CDT tę samą rolę, co model Java w JDT.
Przed pojawieniem się Handly, Eclipse nie oferował specjalistycznych bibliotek do budowy językowych modeli opartych na uchwytach. Istniejące obecnie modele były tworzone głównie poprzez bezpośrednią adaptację kodu modelu Java (zwane również kopiowaniem/wklejaniem), w przypadkach, gdy było to możliwe Eclipse Public License (EPL). (Jasne jest, że na przykład w przypadku projektów samego Eclipse zwykle nie stanowi to problemu z punktu widzenia prawnego, co nie można powiedzieć o produktach z zamkniętym kodem źródłowym.) Oprócz charakterystycznej dla niej bezsystemowości, taka metodologia prowadzi do dobrze znanych problemów: duplikacji kodu, wprowadzonych podczas adaptacji błędów itp. Co gorsza, powstałe modele pozostają "rzeczą samą w sobie" i nie wykorzystują istniejącego potencjału do unifikacji. A przecież wydzielenie wspólnych pojęć i protokołów dla językowych modeli opartych na uchwytach mogłoby doprowadzić do stworzenia komponentów wielokrotnego użytku do pracy z nimi, podobnie jak miało to miejsce w przypadku EMF.
Nie można powiedzieć, że w Eclipse nie rozumiano tych problemów. Już w 2005 roku , podsumowując doświadczenie z tworzenia prototypu CDT, konieczność stworzenia wspólnej infrastruktury dla modeli językowych, w tym modeli opartych na handle. Jednak, jak to często bywa, z powodu bardziej priorytetowych zadań realizacja tych pomysłów nie doszła do skutku. Tymczasem, faktoryzacja kodu *DT-projektów wciąż pozostaje jednym z niewystarczająco opracowanych tematów w Eclipse.
W pewnym sensie, projekt Handly ma na celu rozwiązanie podobnych problemów do EMF, ale dla modeli opartych na handle, a przede wszystkim językowych (tj. przedstawiających elementy struktury jakiegoś języka programowania). Poniżej przedstawiono główne cele, które przyświecały projektowi Handly:
- Wydzielenie głównych abstrakcji obszaru merytorycznego.
- Zredukowanie wysiłku i podniesienie jakości realizacji językowych modeli opartych na handle dzięki wielokrotnemu wykorzystaniu kodu.
- Dostarczenie ujednoliconego API na poziomie meta do powstałych modeli, co umożliwia tworzenie wspólnych komponentów IDE działających z językowymi modelami opartymi na handle.
- Elastyczność i skalowalność.
- Integracja z Xtext (w oddzielnej warstwie).
Aby wydzielić wspólne pojęcia i protokoły, przeanalizowano istniejące realizacje językowych modeli opartych na handle. Główne interfejsy i podstawowe realizacje dostarczane przez Handly są pokazane na rys. 8.

Rys. 8. Wspólne interfejsy i podstawowe realizacje elementów Handly
Interfejs IElement reprezentuje handle elementu i jest wspólny dla elementów wszystkich modeli opartych na Handly. Abstrakcyjna klasa Element implementuje zgeneralizowany mechanizm handle/body (rys. 9).

Rys. 9. IElement i zgeneralizowana realizacja handle/body
Ponadto, Handly dostarcza zgeneralizowany mechanizm powiadamiania o zmianach elementów modelu (rys. 10). Jak widać, ogólnie rzecz biorąc, jest on analogiczny do mechanizmów powiadamiania realizowanych w modelu zasobów i modelu Java, i wykorzystuje IElementDelta do ujednoliconego przedstawienia informacji o zmianie elementu.

Rys. 10. Wspólne interfejsy i podstawowe realizacje mechanizmu powiadamiania Handly
Rozważana powyżej część Handly (rys. 9 i 10) może być używana do reprezentacji praktycznie dowolnych modeli opartych na handle. Aby stworzyć modele językowe projekt oferuje dodatkową funkcjonalność – w szczególności wspólne interfejsy i podstawowe realizacje dla elementów struktury źródłowego tekstu, tzw. source elements (rys. 8). Interfejs ISourceFile reprezentuje plik źródłowy, a ISourceConstruct – element w obrębie pliku źródłowego. Abstrakcyjne klasy SourceFile i SourceConstruct realizują ogólne mechanizmy wspierające pracę z plikami źródłowymi i ich elementami, takie jak obsługa buforów tekstowych, powiązanie z koordynatami elementu w źródłowym tekście, uzgadnianie modelu z bieżącą zawartością kopii roboczej, itp. Realizacja tych mechanizmów zazwyczaj stanowi dość złożone zadanie, a Handly może znacznie skrócić wysiłki związane z tworzeniem modeli opartych na uchwytach dzięki dostarczeniu solidnych podstawowych implementacji.
Oprócz wymienionych powyżej podstawowych mechanizmów, Handly zapewnia infrastrukturę buforów tekstowych oraz „zrzutów” (snapshots), wsparcie dla integracji z edytorami kodu źródłowego (w tym zrealizowaną „od ręki” integrację z edytorem Xtext), a także kilka ogólnych komponentów UI, które współpracują z modelami opartymi na Handly, takich jak framework outline. Aby zilustrować swoje możliwości, projekt dostarcza kilka przykładów, w tym implementację modelu Java na Handly. (W porównaniu do pełnej realizacji modelu Java w JDT, ten model jest celowo uproszczony dla większej przejrzystości.)
Jak wspomniano wcześniej, przy początkowym projektowaniu Handly oraz jego dalszym rozwoju zwrócono poważną uwagę na skalowalność i elastyczność.
Zasadniczo modele oparte na uchwytach są dość dobrze skalowalne „z definicji”. Na przykład idiom handle/body pozwala ograniczyć ilość pamięci używanej przez model. Ale są też niuanse. Podczas testów Handly pod kątem skalowalności odkryto problem w realizacji mechanizmu powiadamiania – przy zmianie dużej liczby elementów budowanie delta zajmowało zbyt dużo czasu. Okazało się, że ten sam problem występuje także w modelu Java w JDT, z którego wówczas zaadaptowano odpowiedni kod. Poprawiliśmy błąd w Handly i przygotowaliśmy podobną łatkę dla JDT, która została z wdzięcznością przyjęta. To tylko jeden z przykładów, kiedy wdrożenie Handly w istniejące modele mogłoby być potencjalnie użyteczne, ponieważ w takim przypadku taki błąd można byłoby naprawić tylko w jednym miejscu.
Aby wdrożenie Handly w istniejących implementacjach modeli było technicznie możliwe, biblioteka musi charakteryzować się znaczną elastycznością. Głównym wyzwaniem jest zachowanie zgodności z API modelu. To zadanie zostało rozwiązane w poprzez wyraźne oddzielenie specyficznego dla modelu API, definiowanego i w pełni kontrolowanego przez dewelopera, od zunifikowanego API meta-poziomu, dostarczanego przez bibliotekę. To nie tylko czyni technicznie możliwym wdrożenie Handly w istniejące implementacje, ale także daje deweloperowi nowego modelu znaczną swobodę w projektowaniu API.
Elastyczność ma również inne aspekty. Na przykład, Handly niemal w ogóle nie narzuca ograniczeń na strukturę modelu i może być używana zarówno do modelowania języków ogólnego przeznaczenia, jak i języków zorientowanych na konkretne przedmioty. Przechodząc do struktury pliku źródłowego, Handly nie narzuca żadnej konkretnej formy reprezentacji AST i w zasadzie nie wymaga nawet obecności AST, zapewniając tym samym kompatybilność z praktycznie wszelkimi mechanizmami analizy składniowej. Wreszcie, Handly wspiera pełną integrację z środowiskiem Eclipse, ale może także działać bezpośrednio z systemami plików, dzięki integracji z (EFS).
Obecna wersja została wydana w grudniu 2016 roku. Mimo że projekt jest obecnie w fazie inkubacji i API nie zostało jeszcze ostatecznie ustalone, Handly jest już używane w dwóch dużych komercyjnych produktach, które odważyły się wystąpić w roli „wczesnych adoptersów”, i, trzeba przyznać, jak dotąd nie żałują tej decyzji.
Jak wspomniano wcześniej, jednym z tych produktów są 1C:Enterprise Development Tools, gdzie Handly od samego początku jest używane do modelowania elementów wysokopoziomowej struktury języków 1C:Przedsiębiorstwo, takich jak wbudowany język programowania oraz język zapytań. Drugi produkt jest mniej znany szerszej publiczności. To , zintegrowane środowisko projektowania procesorów specyficznych dla aplikacji (application-specific instruction-set processor, ASIP), używane zarówno przez czeską firmę Codasip, jak i jej klientów, wśród których znajdują się , , , . Codasip używa Handly w produkcji od 2015 roku, zaczynając od wersji Handly 0.2. Najnowsze wydanie Codasip Studio obecnie wykorzystuje wersję 0.5, wydaną w czerwcu 2016 roku. Ondřej Ilčík, odpowiadający za rozwój IDE w Codasip, jest w kontakcie z projektem, zapewniając niezwykle cenne informacje zwrotne ze strony „zewnętrznego adobtera”. Udało mu się nawet znaleźć trochę wolnego czasu, aby bezpośrednio uczestniczyć w rozwoju projektu, implementując warstwę UI (~ 4000 linii kodu) dla jednego z przykładów Handly, modelu Java. Bardziej szczegółowe informacje „z pierwszej ręki” o używaniu Handly przez adobterów można znaleźć na stronie projektu.
Mamy nadzieję, że po wydaniu wersji 1.0 z gwarancją stabilności API i zakończeniu inkubacji projektu, Handly zyska nowych adobterów. Na razie projekt kontynuuje testy i dalsze doskonalenie API, wydając po dwa „duże” wydania każdego roku – w czerwcu (w tym samym terminie, co jednoczesne wydanie Eclipse) i w grudniu, zapewniając przewidywalny harmonogram, na który mogą liczyć adobterzy. Warto dodać, że wskaźnik „bug rate” projektu pozostaje na stabilnie niskim poziomie, a Handly od pierwszych wersji niezawodnie działa w produktach wczesnych adobterów. Do dalszego zapoznania się z Eclipse Handly można wykorzystać i .
Źródło: habr.com
