Ndoshta,
Në këtë artikull, i cili ka natyrë të përgjithshme, ne do të përpiqemi të shikojmë disa nga bazat e arkitekturës Eclipse si një platformë për ndërtimin e mjeteve të integruara të zhvillimit dhe të japim një ide fillestare të komponentëve të Eclipse që formojnë themelin e teknologjisë. platforma për "Konfiguratorin e ri" 1C: Ndërmarrja.
Hyrje në Arkitekturën Eclipse
Le të shohim së pari disa aspekte të përgjithshme të arkitekturës së Eclipse duke përdorur shembullin
Para së gjithash, duhet të theksohet se Eclipse karakterizohet nga një shtresëzim mjaft i qartë arkitektonik, me ndarjen e funksionalitetit të pavarur nga gjuha nga funksionaliteti i krijuar për të mbështetur gjuhë të veçanta programimi, dhe ndarjen e komponentëve "bërthamë" të pavarur nga UI nga komponentët e lidhur. me ndërfaqen mbështetëse të përdoruesit.
Kështu, Platforma Eclipse përcakton një infrastrukturë të përbashkët, të pavarur nga gjuha, dhe mjetet e zhvillimit të Java i shtojnë Eclipse një Java IDE me funksione të plota. Si platforma Eclipse ashtu edhe JDT përbëhen nga disa komponentë, secili prej të cilëve i përket ose një "bërthame" të pavarur nga UI ose një shtrese UI (Figura 1).
Oriz. 1. Eclipse Platform dhe JDT
Le të rendisim përbërësit kryesorë të Platformës Eclipse:
- Runtime — Përcakton infrastrukturën e shtojcave. Eklipsi karakterizohet nga një arkitekturë modulare. Në thelb, Eclipse është një koleksion i "pikave të zgjatjes" dhe "zgjatjeve".
- Hapësira e punës — Menaxhon një ose më shumë projekte. Një projekt përbëhet nga dosje dhe skedarë që janë hartuar drejtpërdrejt në sistemin e skedarëve.
- Paketa standarde e mjeteve të miniaplikacioneve (SWT) - Ofron elemente bazë të ndërfaqes së përdoruesit të integruar me sistemin operativ.
- JFace — Ofron një numër kornizash UI të ndërtuara në krye të SWT.
- Workbench — Përcakton paradigmën Eclipse UI: redaktorët, pikëpamjet, perspektivat.
Duhet thënë se Platforma Eclipse ofron gjithashtu shumë komponentë të tjerë të dobishëm për ndërtimin e mjeteve të integruara të zhvillimit, duke përfshirë Debug, Krahasim, Search dhe Team. Duhet përmendur veçanërisht JFace Text - baza për ndërtimin e "redaktorëve të zgjuar" të kodit burimor. Fatkeqësisht, edhe një ekzaminim i përciptë i këtyre komponentëve, si dhe i komponentëve të shtresës UI, nuk është i mundur brenda fushës së këtij artikulli, kështu që në pjesën e mbetur të këtij seksioni do të kufizohemi në një përmbledhje të komponentëve kryesorë "thelbësorë" të Platforma Eclipse dhe JDT.
Core Runtime
Infrastruktura e shtojcave Eclipse bazohet në
Hapësira kryesore e punës
Pothuajse çdo mjedis zhvillimi i integruar i ndërtuar në krye të Platformës Eclipse funksionon me hapësirën e punës Eclipse. Është hapësira e punës që zakonisht përmban kodin burimor të aplikacionit të zhvilluar në IDE. Hapësira e punës hartohet drejtpërdrejt në sistemin e skedarëve dhe përbëhet nga projekte që përmbajnë dosje dhe skedarë. Këto projekte, dosje dhe skedarë quhen resursami hapësirën e punës. Zbatimi i hapësirës së punës në Eclipse shërben si një cache në lidhje me sistemin e skedarëve, gjë që bën të mundur përshpejtimin e ndjeshëm të kalimit të pemës së burimeve. Përveç kësaj, hapësira e punës ofron një sërë shërbimesh shtesë, duke përfshirë
Komponenti Core Resources (org.eclipse.core.resources plugin) është përgjegjës për mbështetjen e hapësirës së punës dhe burimeve të saj. Në veçanti, ky komponent ofron qasje programatike në hapësirën e punës në formë modelet e burimeve. Për të punuar në mënyrë efektive me këtë model, klientët kanë nevojë për një mënyrë të thjeshtë për të paraqitur një lidhje me një burim. Në këtë rast, do të ishte e dëshirueshme të fshihej objekti që ruan drejtpërdrejt gjendjen e burimit në model nga aksesi i klientit. Përndryshe, në rastin e, për shembull, fshirjes së një skedari, klienti mund të vazhdojë të mbajë një objekt që nuk është më në model, me problemet që pasojnë. Eclipse e zgjidh këtë problem duke përdorur diçka të quajtur trajtuar burimi. Handle vepron si një çelës (ai e njeh vetëm rrugën drejt burimit në hapësirën e punës) dhe kontrollon plotësisht aksesin në objektin e modelit të brendshëm, i cili ruan drejtpërdrejt informacionin për gjendjen e burimit. Ky dizajn është një variant i modelit
Oriz. Figura 2 ilustron idiomën Handle/Body siç aplikohet në modelin e burimit. Ndërfaqja IResource përfaqëson dorezën e një burimi dhe është një API, ndryshe nga klasa Resource, e cila zbaton këtë ndërfaqe, dhe klasa ResourceInfo, e cila përfaqëson trupin, të cilat nuk janë API. Theksojmë se handle njeh vetëm shtegun drejt burimit në lidhje me rrënjën e hapësirës së punës dhe nuk përmban një lidhje me informacionin e burimit. Objektet e informacionit të burimeve formojnë të ashtuquajturën "pemë elementare". Kjo strukturë e të dhënave është materializuar plotësisht në memorie. Për të gjetur shembullin e informacionit të burimit që korrespondon me një dorezë, pema e elementit përshkohet sipas shtegut të ruajtur në atë dorezë.
Oriz. 2. IRResource dhe ResourceInfo
Siç do ta shohim më vonë, dizajni bazë i modelit të burimeve (mund ta quajmë të bazuar në dorezë) përdoret në Eclipse edhe për modele të tjera. Tani për tani, le të rendisim disa nga vetitë dalluese të këtij dizajni:
- Doreza është një objekt me vlerë. Objektet e vlerës janë objekte të pandryshueshme, barazia e të cilave nuk bazohet në identitet. Objekte të tilla mund të përdoren në mënyrë të sigurtë si çelës në kontejnerë të copëtuar. Instanca të shumta të dorezës mund të referojnë të njëjtin burim. Për t'i krahasuar ato, duhet të përdorni metodën e barabartë (Object).
- Handle përcakton sjelljen e një burimi, por nuk përmban informacion në lidhje me gjendjen e burimit (e vetmja e dhënë që ruan është "çelësi", rruga drejt burimit).
- Handle mund t'i referohet një burimi që nuk ekziston (ose një burim që nuk është krijuar ende, ose një burim që tashmë është fshirë). Ekzistenca e një burimi mund të kontrollohet duke përdorur metodën IResource.exists().
- Disa operacione mund të zbatohen bazuar vetëm në informacionin e ruajtur në vetë dorezën (të ashtuquajturat operacione vetëm me dorezë). Shembuj janë IResource.getParent(), getFullPath(), etj. Burimi nuk ka nevojë të ekzistojë që një operacion i tillë të ketë sukses. Operacionet që kërkojnë të ekzistojë një burim për të pasur sukses hedhin një CoreException nëse burimi nuk ekziston.
Eclipse ofron një mekanizëm efikas për njoftimin e ndryshimeve të burimeve të hapësirës së punës (Figura 3). Burimet mund të ndryshojnë ose si rezultat i veprimeve të kryera brenda vetë Eclipse IDE ose si rezultat i sinkronizimit me sistemin e skedarëve. Në të dyja rastet, klientët që abonohen në njoftime u jepet informacion i detajuar në lidhje me ndryshimet në formën e "deltas së burimeve". Një delta përshkruan ndryshimet midis dy gjendjeve të një (nën-)peme të burimit të hapësirës së punës dhe është në vetvete një pemë, secila nyje e së cilës përshkruan një ndryshim në një burim dhe përmban një listë deltash në nivelin tjetër që përshkruajnë ndryshimet në burimet fëmijë.
Oriz. 3. IResourceChangeEvent dhe IResourceDelta
Mekanizmi i njoftimit i bazuar në deltat e burimeve ka karakteristikat e mëposhtme:
- Një ndryshim i vetëm dhe shumë ndryshime përshkruhen duke përdorur të njëjtën strukturë, pasi delta është ndërtuar duke përdorur parimin e përbërjes rekursive. Klientët e pajtimtarëve mund të përpunojnë njoftimet për ndryshimin e burimeve duke përdorur zbritjen rekursive përmes një peme deltash.
- Delta përmban informacion të plotë në lidhje me ndryshimet në burim, duke përfshirë lëvizjen e tij dhe/ose ndryshimet në "shënuesit" që lidhen me të (për shembull, gabimet e përpilimit përfaqësohen si shënues).
- Meqenëse referencat e burimeve bëhen përmes dorezës, delta mund t'i referohet natyrshëm një burimi të largët.
Siç do të shohim së shpejti, komponentët kryesorë të dizajnit të mekanizmit të njoftimit të ndryshimit të modelit të burimeve janë gjithashtu të rëndësishme për modelet e tjera të bazuara në dorezë.
Bërthama JDT
Modeli i burimeve të hapësirës së punës Eclipse është një model themelor gjuhësor-agnostik. Komponenti JDT Core (plugin org.eclipse.jdt.core) ofron një API për lundrimin dhe analizimin e strukturës së hapësirës së punës nga një këndvështrim Java, i ashtuquajturi "modeli Java" (Modeli Java). Ky API përcaktohet në terma të elementeve Java, në krahasim me modelin themelor të burimit API, i cili përcaktohet në lidhje me dosjet dhe skedarët. Ndërfaqet kryesore të pemës së elementit Java janë paraqitur në Fig. 4.
Oriz. 4. Elementet e modelit Java
Modeli Java përdor të njëjtën idiomë doreze/trupi si modeli i burimeve (Figura 5). IJavaElement është doreza, dhe JavaElementInfo luan rolin e trupit. Ndërfaqja IJavaElement përcakton një protokoll të përbashkët për të gjithë elementët Java. Disa nga metodat e tij janë vetëm për përdorim: getElementName(), getParent(), etj. Objekti JavaElementInfo ruan gjendjen e elementit përkatës: strukturën dhe atributet e tij.
Oriz. 5. IJavaElement dhe JavaElementInfo
Modeli Java ka disa ndryshime në zbatimin e modelit bazë të dorezës/trupit në krahasim me modelin e burimeve. Siç u përmend më lart, në modelin e burimeve, pema e elementit, nyjet e së cilës janë objekte të informacionit të burimit, është tërësisht e përfshirë në memorie. Por modeli Java mund të ketë një numër dukshëm më të madh elementesh sesa pema e burimeve, sepse ai gjithashtu përfaqëson strukturën e brendshme të skedarëve .java dhe .class: llojet, fushat dhe metodat.
Për të shmangur materializimin e plotë të të gjithë pemës së elementeve në memorie, zbatimi i modelit Java përdor një cache LRU me madhësi të kufizuar të informacionit të elementit, ku çelësi është trajtimi IJavaElement. Objektet e informacionit të elementit krijohen sipas kërkesës ndërsa navigohet pema e elementeve. Në këtë rast, artikujt më pak të përdorur largohen nga cache dhe konsumi i memories së modelit mbetet i kufizuar në madhësinë e specifikuar të cache. Ky është një tjetër avantazh i dizajnit të bazuar në dorezë, i cili fsheh plotësisht detaje të tilla të zbatimit nga kodi i klientit.
Mekanizmi për njoftimin e ndryshimeve në elementët Java është në përgjithësi i ngjashëm me mekanizmin për gjurmimin e ndryshimeve në burimet e hapësirës së punës të diskutuar më sipër. Një klient që dëshiron të monitorojë ndryshimet në modelin Java pajtohet në njoftime, të cilat përfaqësohen si një objekt ElementChangedEvent që përmban një IJavaElementDelta (Figura 6).
Oriz. 6. ElementChangedEvent dhe IJavaElementDelta
Modeli Java nuk përmban informacion në lidhje me trupat e metodës ose rezolucionin e emrit, kështu që për analizën e detajuar të kodit të shkruar në Java, JDT Core ofron një model shtesë (jo i bazuar në dorezë):
Për shkak se pemët sintaksore mund të konsumojnë një sasi të konsiderueshme memorie, JDT ruan vetëm një AST për redaktuesin aktiv. Ndryshe nga modeli Java, AST zakonisht shihet si një model "i ndërmjetëm", "i përkohshëm", anëtarët e të cilit nuk duhet të referohen nga klientët jashtë kontekstit të operacionit që çoi në krijimin e AST.
Tre modelet e listuara (modeli Java, AST, lidhjet) së bashku formojnë bazën për ndërtimin e "veglave inteligjente të zhvillimit" në JDT, duke përfshirë një redaktues të fuqishëm Java me "ndihmues" të ndryshëm, veprime të ndryshme për përpunimin e kodit burimor (përfshirë organizimin e një liste importi emrat dhe formatimi sipas stilit të personalizuar), mjetet e kërkimit dhe rifaktorimit. Në këtë rast, modeli Java luan një rol të veçantë, pasi është ai që përdoret si bazë për një paraqitje vizuale të strukturës së aplikacionit që po zhvillohet (për shembull, në Package Explorer, Outline, Search, Call Hierarky, dhe Tipi Hierarkia).
Komponentët e eklipsit të përdorur në 1C: Veglat e Zhvillimit të Ndërmarrjeve
Në Fig. Figura 7 tregon komponentët Eclipse që formojnë themelin e platformës së teknologjisë për 1C: Mjetet e Zhvillimit të Ndërmarrjeve.
Oriz. 7. Eclipse si një platformë për 1C: Enterprise Development Tools
Platforma Eclipse ofron infrastrukturën bazë. Ne shikuam disa aspekte të kësaj infrastrukture në seksionin e mëparshëm.
Ashtu si çdo mjet vërtet me qëllim të përgjithshëm, EMF është i përshtatshëm për zgjidhjen e një game të gjerë problemesh modelimi, por disa klasa modelesh (për shembull, modelet e bazuara në dorezë të diskutuar më lart) mund të kërkojnë mjete modelimi më të specializuara. Të flasësh për EMF është një detyrë e pafalshme, veçanërisht brenda kufijve të kufizuar të një artikulli, pasi ky është subjekt i një libri të veçantë dhe mjaft të trashë. Le të theksojmë vetëm se sistemi me cilësi të lartë të përgjithësimeve që qëndron në themel të EMF lejoi lindjen e një game të tërë projektesh kushtuar modelimit, të cilat përfshihen në projektin e nivelit të lartë
1C: Mjetet e Zhvillimit të Ndërmarrjeve përdorin në mënyrë aktive si vetë EMF ashtu edhe një numër projektesh të tjera të Modelimit Eclipse. Në veçanti, Xtext është një nga themelet e mjeteve të zhvillimit për gjuhë të tilla 1C: Enterprise si gjuha e integruar e programimit dhe gjuha e pyetjeve. Një bazë tjetër për këto mjete zhvillimi është projekti Eclipse Handly, të cilin do ta diskutojmë më në detaje (nga përbërësit e listuar Eclipse, është ende më pak i njohur).
Parimet bazë arkitekturore të modeleve të bazuara në dorezë, të tilla si idioma e dorezës/trupit, u diskutuan më lart duke përdorur modelin e burimeve dhe modelin Java si shembuj. Ai gjithashtu vuri në dukje se si modeli i burimeve ashtu edhe modeli Java janë themele të rëndësishme për mjetet e zhvillimit të Eclipse Java (JDT). Dhe duke qenë se pothuajse të gjitha projektet *DT Eclipse kanë një arkitekturë të ngjashme me JDT, nuk do të ishte një ekzagjerim i madh të thuhet se modelet e bazuara në dorezë janë në themel të shumë, nëse jo të gjitha IDE-ve të ndërtuara në krye të Platformës Eclipse. Për shembull, Eclipse C/C++ Development Tooling (CDT) ka një model C/C++ të bazuar në dorezë që luan të njëjtin rol në arkitekturën CDT si modeli Java në JDT.
Përpara Handly, Eclipse nuk ofronte biblioteka të specializuara për ndërtimin e modeleve gjuhësore të bazuara në doreza. Modelet që ekzistojnë aktualisht janë krijuar kryesisht duke përshtatur drejtpërdrejt kodin e modelit Java (aka copy/paste), në rastet kur lejon Eclipse Public License (EPL). (Natyrisht, kjo zakonisht nuk është një çështje ligjore për, të themi, vetë projektet Eclipse, por jo për produktet me burim të mbyllur.) Përveç rastësisë së saj të natyrshme, kjo teknikë paraqet probleme të njohura: dyfishimi i kodit i paraqitur nga kur përshtatet me gabimet, etj. Ajo që është më e keqja është se modelet që rezultojnë mbeten "gjëra në vetvete" dhe nuk përfitojnë nga potenciali për bashkim. Por izolimi i koncepteve dhe protokolleve të përbashkëta për modelet gjuhësore të bazuara në dorezë mund të çojë në krijimin e komponentëve të ripërdorshëm për të punuar me to, ngjashëm me atë që ndodhi në rastin e EMF.
Nuk është se Eclipse nuk i kuptoi këto çështje. Në vitin 2005
Në një farë kuptimi, projekti Handly është krijuar për të zgjidhur përafërsisht të njëjtat probleme si EMF, por për modelet e bazuara në dorezë, dhe kryesisht ato gjuhësore (d.m.th., që përfaqësojnë elemente të strukturës së disa gjuhëve programuese). Qëllimet kryesore të vendosura gjatë dizajnimit të Handly janë renditur më poshtë:
- Identifikimi i abstraksioneve kryesore të fushës lëndore.
- Reduktimi i përpjekjeve dhe përmirësimi i cilësisë së zbatimit të modeleve gjuhësore të bazuara në doreza përmes ripërdorimit të kodit.
- Sigurimi i një API të unifikuar të meta-nivelit për modelet që rezultojnë, duke bërë të mundur krijimin e komponentëve të përbashkët IDE që funksionojnë me modelet e bazuara në dorezë gjuhësore.
- Fleksibiliteti dhe shkallëzueshmëria.
- Integrimi me Xtext (në një shtresë të veçantë).
Për të nxjerrë në pah konceptet dhe protokollet e përbashkëta, u analizuan implementimet ekzistuese të modeleve të bazuara në dorezë gjuhësore. Ndërfaqet kryesore dhe implementimet bazë të ofruara nga Handly janë paraqitur në Fig. 8.
Oriz. 8. Ndërfaqet e përbashkëta dhe implementimet bazë të elementeve Handly
Ndërfaqja IElement përfaqëson dorezën e një elementi dhe është e zakonshme për elementët e të gjitha modeleve të bazuara në Handly. Elementi i klasës abstrakte zbaton mekanizmin e përgjithësuar të dorezës/trupit (Fig. 9).
Oriz. 9. Implementimi i dorezës/trupit IElement dhe gjenerik
Përveç kësaj, Handly ofron një mekanizëm të përgjithësuar për njoftimin për ndryshimet në elementët e modelit (Fig. 10). Siç mund ta shihni, është gjerësisht i ngjashëm me mekanizmat e njoftimit të zbatuar në modelin e burimit dhe modelin Java, dhe përdor IElementDelta për të ofruar një paraqitje të unifikuar të informacionit të ndryshimit të elementit.
Oriz. 10. Ndërfaqet e përgjithshme dhe implementimet bazë të mekanizmit të njoftimit Handly
Pjesa Handly e diskutuar më sipër (Fig. 9 dhe 10) mund të përdoret për të përfaqësuar pothuajse çdo model të bazuar në dorezë. Për krijimin gjuhësor modele, projekti ofron funksionalitet shtesë - në veçanti, ndërfaqe të zakonshme dhe zbatime bazë për elementët e strukturës së tekstit burimor, të ashtuquajturat elementet burimore (Fig. 8). Ndërfaqja ISourceFile përfaqëson një skedar burim, dhe ISourceConstruct përfaqëson një element brenda skedarit burim. Klasat abstrakte SourceFile dhe SourceConstruct zbatojnë mekanizma të përgjithësuar për të mbështetur punën me skedarët burimor dhe elementet e tyre, për shembull, puna me buferët e tekstit, lidhjen me koordinatat e një elementi në tekstin burimor, harmonizimin e modeleve me përmbajtjen aktuale të një buferi të kopjimit të punës. , etj. Zbatimi i këtyre mekanizmave është zakonisht mjaft sfidë dhe Handly mund të zvogëlojë ndjeshëm përpjekjet e zhvillimit të modeleve gjuhësore të bazuara në doreza duke ofruar implementime bazë me cilësi të lartë.
Përveç mekanizmave thelbësorë të listuar më sipër, Handly ofron një infrastrukturë për buferët e tekstit dhe fotografitë, mbështetje për integrimin me redaktuesit e kodit burimor (duke përfshirë integrimin jashtë kutisë me redaktorin Xtext), si dhe disa komponentë të zakonshëm të ndërfaqes që punoni me redaktorët e kodit burimor. Për të ilustruar aftësitë e tij, projekti ofron disa shembuj, duke përfshirë një implementim të modelit Java në Handly. (Krahasuar me zbatimin e plotë të modelit Java në JDT, ky model është thjeshtuar qëllimisht disi për qartësi më të madhe.)
Siç u përmend më herët, një fokus i madh gjatë dizajnit fillestar dhe zhvillimit të mëpasshëm të Handly ishte dhe vazhdon të jetë në shkallëzueshmërinë dhe fleksibilitetin.
Në parim, modelet e bazuara në dorezë shkallëzohen mjaft mirë "sipas dizajnit". Për shembull, idioma e dorezës/trupit ju lejon të kufizoni sasinë e memories së konsumuar nga një model. Por ka edhe nuanca. Kështu, gjatë testimit të Handly për shkallëzueshmërinë, u zbulua një problem në zbatimin e mekanizmit të njoftimit - kur një numër i madh elementësh u ndryshuan, ndërtimi i deltat mori shumë kohë. Doli se i njëjti problem ishte i pranishëm në modelin JDT Java, nga i cili dikur u përshtat kodi përkatës. Ne rregulluam defektin në Handly dhe përgatitëm një patch të ngjashëm për JDT, i cili u prit me mirënjohje. Ky është vetëm një shembull ku futja e Handly në implementimet ekzistuese të modeleve mund të jetë potencialisht e dobishme, sepse në këtë rast një gabim i tillë mund të rregullohet vetëm në një vend.
Për ta bërë teknikisht të realizueshme zbatimin e Handly në zbatimin e modeleve ekzistuese, biblioteka duhet të ketë fleksibilitet të konsiderueshëm. Problemi kryesor është ruajtja e përputhshmërisë së prapambetur në të gjithë modelin API. Ky problem u zgjidh në
Fleksibiliteti ka edhe aspekte të tjera. Për shembull, Handly nuk vendos pothuajse asnjë kufizim në strukturën e modelit dhe mund të përdoret për të modeluar gjuhët për qëllime të përgjithshme dhe specifike për domenin. Kur ndërton strukturën e skedarit burimor, Handly nuk përshkruan ndonjë formë të veçantë të paraqitjes së AST dhe, në parim, nuk kërkon as praninë e vetë një AST, duke siguruar kështu pajtueshmërinë me pothuajse çdo mekanizëm analizues. Së fundi, Handly mbështet integrimin e plotë me hapësirën e punës Eclipse, por gjithashtu mund të punojë drejtpërdrejt me sistemet e skedarëve falë integrimit të tij me
Versioni aktual
Siç u përmend më lart, një nga këto produkte është 1C: Enterprise Development Tools, ku Handly përdoret që në fillim për të modeluar elementë të strukturës së nivelit të lartë të gjuhëve të tilla 1C: Enterprise si gjuha e integruar e programimit dhe gjuha e pyetjeve. . Një produkt tjetër është më pak i njohur për publikun e gjerë. Kjo
Shpresojmë që pas lëshimit të versionit 1.0 me garanci për stabilitetin e API-së dhe largimit të projektit nga gjendja e inkubacionit, Handly do të ketë adoptues të rinj. Ndërkohë, projekti vazhdon të testojë dhe përmirësojë më tej API-në, duke lëshuar dy lëshime "të mëdha" në vit - në qershor (të njëjtën datë me lëshimin e njëkohshëm të Eclipse) dhe dhjetor, duke siguruar një orar të parashikueshëm ku mund të mbështeten adoptuesit. Mund të shtojmë gjithashtu se "shkalla e gabimeve" të projektit mbetet në një nivel vazhdimisht të ulët dhe Handly ka punuar me besueshmëri në produktet e adoptuesve të hershëm që në versionet e para. Për të eksploruar më tej Eclipse Handly, mund të përdorni
Burimi: www.habr.com