Probably,
In this review article, we will try to consider some of the basics of the Eclipse architecture as a platform for building integrated development tools and give an initial idea of the Eclipse components that form the foundation of the technological platform for the “new Configurator” 1C: Enterprise,
Introduction to the Eclipse architecture
Let's first look at some general aspects of the Eclipse architecture using an example
First of all, it should be noted that Eclipse is characterized by a fairly clear architectural stratification, with the separation of language-independent functionality from the functionality designed to support specific programming languages, and the separation of UI-independent "core" (core) components from components related to user support. interface.
For example, the Eclipse Platform defines a common, language-independent infrastructure, and the Java development tools add a full-featured Java IDE to Eclipse. Both the Eclipse Platform and the JDT are made up of several components, each of which is either a UI-independent "core" or a UI layer (Figure 1).
Rice. 1. Eclipse Platform and JDT
We list the main components of the Eclipse Platform:
- Runtime - Defines the plugin infrastructure. Eclipse has a modular architecture. Essentially, Eclipse is a collection of "extension points" and "extensions".
- workspace - Manages one or more projects. A project consists of folders and files that map directly to the file system.
- Standard Widget Toolkit (SWT) - Provides basic user interface elements that are integrated with the operating system.
- JFace - Provides a range of UI frameworks built on top of SWT.
- Workbench - Defines the Eclipse UI paradigm: editors, views, perspectives.
I must say that the Eclipse Platform provides many other useful components for building integrated development tools, among which are Debug, Compare, Search, and Team. We should also mention JFace Text - the basis for building "smart editors" of the source code. Unfortunately, even a cursory review of these components, as well as the UI layer component, is not possible within the framework of this article, so in the rest of this section we will limit ourselves to reviewing the main "core" components of the Eclipse Platform and JDT.
core runtime
The Eclipse Plugin Framework is based on
core workspace
Almost any IDE built on top of the Eclipse Platform works with the Eclipse workspace. It is the workspace that usually contains the source code of the application being developed in the IDE. Workspace maps directly to the file system and consists of projects that contain folders and files. These projects, folders, and files are called the resources workspace. The implementation of workspace in Eclipse serves as a cache in relation to the file system, which can significantly speed up resource tree traversal. In addition, workspace provides a number of additional services, including
The Core Resources component (org.eclipse.core.resources plugin) is responsible for supporting the workspace and its resources. In particular, this component provides programmatic access to the workspace in the form resource models. To work effectively with this model, clients need a simple way to represent a link to a resource. At the same time, it would be desirable to hide the object that directly stores the state of the resource in the model from client access. Otherwise, in the case of, for example, deleting a file, the client could continue to hold an object that is no longer in the model, with the ensuing problems. Eclipse solves this problem using the so-called handle resource. Handle acts as a key (it only knows the path to the resource in the workspace) and completely controls access to the internal model object, which directly stores information about the state of the resource. This design is a variation of the pattern
Rice. 2 illustrates the Handle/Body idiom as applied to a resource model. The IResource interface represents the handle of a resource and is an API, unlike the Resource class that implements this interface, and the ResourceInfo class that represents body, which are not APIs. We emphasize that handle only knows the path to the resource relative to the workspace root and does not contain a link to resource info. Resource info objects form what is known as an "element tree". This data structure is fully materialized in memory. To find a resource info instance corresponding to some handle, the element tree is traversed according to the path stored in that handle.
Rice. 2. IResource and ResourceInfo
As we will see later, the basic design of the resource model (you can call it handle-based) is used in Eclipse and for other models. In the meantime, here are some of the distinguishing features of this design:
- Handle is a value object. Value objects are immutable objects whose equality is not based on identity. Such objects can be safely used as a key in hashed containers. Multiple instances of handle can refer to the same resource. To compare them, use the equals(Object) method.
- Handle defines the behavior of the resource, but does not contain information about the state of the resource (the only data it stores is the "key", the path to the resource).
- Handle can refer to a resource that does not exist (either a resource that has not yet been created, or a resource that has already been deleted). The existence of a resource can be checked using the IResource.exists() method.
- Some operations can be implemented based solely on the information stored in the handle itself (the so-called handle-only operations). Examples are IResource.getParent(), getFullPath(), etc. The resource does not need to exist for such an operation to succeed. Operations that require the resource to exist in order to succeed throw an exception (CoreException) if the resource does not exist.
Eclipse provides an efficient mechanism for notifying changes to workspace resources (Figure 3). Resources can change both as a result of actions performed in the Eclipse IDE itself, and as a result of synchronization with the file system. In both cases, customers who subscribe to the notifications are provided with detailed information about the changes in the form of "resource deltas" (resource delta). A delta describes changes between two states of a workspace resource (sub-)tree and is itself a tree, each node of which describes a change to some resource and contains a list of next-level deltas that describe changes to child resources.
Rice. 3. IResourceChangeEvent and IResourceDelta
The notification mechanism based on resource deltas has the following characteristics:
- A single change and a lot of changes are described using the same structure, since the delta is built according to the principle of recursive composition. Subscriber clients can process resource change notifications by recursively descending the delta tree.
- The delta contains complete information about the change of the resource, including its movement and/or change of the "markers" associated with it (markers represent, for example, compilation errors).
- Because resource references are made through the handle, the delta can naturally refer to a remote resource.
As we will see shortly, the main design elements of the resource model change notification mechanism are relevant for other handle-based models as well.
JDT Core
The Eclipse workspace resource model is a fundamental language-independent model. The JDT Core component (org.eclipse.jdt.core plugin) provides an API for navigating and analyzing the workspace structure from a Java perspective, the so-called "Java model" (java model). This API is defined in terms of Java elements, as opposed to the underlying resource model API, which is defined in terms of folders and files. The main interfaces of the Java element tree are shown in Fig. 4.
Rice. 4. Java Model Elements
The Java model uses the same handle/body idiom as the resource model (Figure 5). IJavaElement is the handle and JavaElementInfo is the body. The IJavaElement interface defines a protocol common to all Java elements. Some of its methods are handle-only: getElementName(), getParent(), and so on. The JavaElementInfo object stores the state of the corresponding element: its structure and attributes.
Rice. 5. IJavaElement and JavaElementInfo
The Java model has some differences in the implementation of the basic handle/body design compared to the resource model. As noted above, in a resource model, an element tree whose nodes are resource info objects is entirely contained in memory. But a Java model can have a much larger number of elements than a resource tree, because it also contains the internal structure of .java and .class files: types, fields, and methods.
To avoid completely materializing the entire element tree in memory, the Java model implementation uses a size-limited element info LRU cache, where the key is handle IJavaElement. Element info objects are created on demand as the element tree is navigated. In this case, the least frequently used elements are evicted from the cache, and the memory consumption of the model remains limited by the specified cache size. This is another advantage of handle-based design, which completely hides such implementation details from client code.
The mechanism for notifying changes to Java elements is broadly similar to the mechanism for tracking workspace resource changes discussed above. A client that wants to track changes in the Java model subscribes to notifications, which are represented as an ElementChangedEvent object that contains an IJavaElementDelta (Figure 6).
Rice. 6.ElementChangedEvent and IJavaElementDelta
The Java model does not contain information about the body of methods or name resolution, so for a detailed analysis of code written in Java, JDT Core provides an additional (non-handle-based) model:
Because syntax trees can consume a significant amount of memory, the JDT caches only one AST, per active editor. Unlike the Java model, the AST is generally viewed as an "intermediate", "temporary" model whose elements should not be retained by clients outside the context of the operation that created the AST.
These three models (Java model, AST, bindings) together form the basis for building “intelligent development tools” in JDT, including a powerful Java editor with various “helpers”, various actions for processing source code (including organizing a name import list and formatting according to the customized style), search and refactoring tools. In this case, the Java model plays a special role, since it is it that is used as the basis for the visual representation of the structure of the developed application (for example, in the Package Explorer, Outline, Search, Call Hierarchy, and Type Hierarchy).
Eclipse components used in 1C:Enterprise Developments Tools
On fig. Figure 7 shows the Eclipse components that form the foundation of the technology platform for 1C:Enterprise Development Tools.
Rice. 7. Eclipse as a platform for 1C:Enterprise Development Tools
Eclipse Platform provides the basic infrastructure. We covered some aspects of this infrastructure in the previous section.
Like any truly versatile tool, EMF is suitable for a wide range of modeling tasks, but some classes of models (for example, the handle-based models discussed above) may need more specialized modeling tools. Talking about EMF is a thankless task, especially within the limited framework of one article, since this is the subject of a separate book, and quite a thick one. We only note that the qualitative system of generalizations underlying the EMF allowed the birth of a whole range of modeling projects that are included in the top-level project
1C:Enterprise Development Tools actively use both EMF itself and a number of other Eclipse Modeling projects. In particular, Xtext is one of the foundations of development tools for such 1C:Enterprise languages as the built-in programming language and query language. Another basis for these development tools is the Eclipse Handly project, which we will dwell on in more detail (of the Eclipse components listed, it is still the least known).
The basic architectural principles of handle-based models, such as the handle/body idiom, were discussed above using the resource model and the Java model as an example. It also noted that both the resource model and the Java model are important foundations for the Eclipse Java development tools (JDT). And since almost all Eclipse *DT projects have an architecture similar to JDT, it would not be a big exaggeration to say that handle-based models underlie many, if not all IDEs built on top of the Eclipse Platform. For example, Eclipse C/C++ Development Tooling (CDT) has a handle-based C/C++ model that plays the same role in the CDT architecture as the Java model does in the JDT.
Prior to Handly, Eclipse did not offer specialized libraries for building handle-based language models. Models that exist today were created mainly by directly adapting the Java model code (aka copy/paste), in cases where it allows Eclipse Public License (EPL). (Obviously, for, say, Eclipse's own projects, this is usually not a legal issue, which is not the case for closed source products.) In addition to its characteristic haphazardness, this technique leads to well-known problems: code duplication introduced by when adapting to mistakes, etc. Even worse, the resulting models remain "things in themselves" and do not use the existing potential for unification. But the allocation of common concepts and protocols for language handle-based models could lead to the creation of reusable components for working with them, similar to what happened in the case of EMF.
This is not to say that Eclipse did not have an understanding of these problems. Back in 2005
In a certain sense, the Handly project is designed to solve approximately the same tasks as EMF, but for handle-based models, and primarily language ones (that is, representing elements of the structure of a programming language). The following are the main design goals for Handly:
- Isolation of the main abstractions of the subject area.
- Reducing the effort and improving the quality of the implementation of language handle-based models through code reuse.
- Providing a unified meta-level API to the resulting models, making it possible to create common IDE components that work with language handle-based models.
- Flexibility and scalability.
- Integration with Xtext (in a separate layer).
To highlight common concepts and protocols, the existing implementations of language handle-based models were analyzed. The main interfaces and base implementations provided by Handly are shown in Figure 8-XNUMX. XNUMX.
Rice. 8. Common interfaces and basic implementations of Handly elements
The IElement interface represents the handle of an element and is common to elements of all Handly-based models. The abstract class Element implements a generic handle/body mechanism (Figure 9).
Rice. 9. IElement and generic implementation of handle/body
In addition, Handly provides a generalized notification mechanism for changing model elements (Fig. 10). As you can see, in general terms, it is similar to the notification mechanisms implemented in the resource model and the Java model, and uses IElementDelta to provide a unified representation of element change information.
Rice. 10. Common interfaces and basic implementations of the Handly notification mechanism
The part of Handly considered above (Fig. 9 and 10) can be used to represent almost any handle-based models. For creating linguistic models, the project offers additional functionality - in particular, common interfaces and base implementations for source text structure elements, the so-called source elements (Fig. 8). The ISourceFile interface represents a source file, and ISourceConstruct represents an element within the source file. The abstract classes SourceFile and SourceConstruct implement generic mechanisms to support working with source files and their elements, for example, working with text buffers, binding to element coordinates in the source text, reconciling the model with the current contents of the working copy buffer, etc. Implementing these mechanisms is usually quite a challenge, and Handly can significantly reduce the effort of developing handle-based language models by providing quality base implementations.
In addition to the core mechanisms listed above, Handly provides a text buffer and snapshot infrastructure, support for integration with source code editors (including out-of-the-box integration with the Xtext editor), as well as some common UI components that work with Handly models such as the outline framework. To illustrate its capabilities, the project provides several examples, including the implementation of the Java model on Handly. (Compared to the full implementation of the Java model in the JDT, this model is intentionally somewhat simplified for better clarity.)
As noted earlier, Handly's initial design and development was and continues to be heavily focused on scalability and flexibility.
In principle, handle-based models scale well “by design”. For example, the handle/body idiom allows you to limit the amount of memory consumed by the model. But there are also nuances. So, when testing Handly for scalability, a problem was found in the implementation of the notification mechanism - when changing a large number of elements, building deltas took too much time. It turned out that the same problem is present in the Java JDT model, from which the corresponding code was adapted at one time. We fixed a bug in Handly and prepared a similar patch for JDT, which was gratefully received. This is just one example where implementing Handly into existing model implementations could be potentially beneficial, as it would be possible to fix such a bug in just one place.
To make embedding Handly into existing model implementations technically feasible, the library must be highly flexible. The main problem is to maintain backward compatibility with the model API. This problem was solved in
Flexibility has other aspects as well. For example, Handly imposes almost no restrictions on the structure of the model and can be used to model general-purpose languages as well as domain-specific languages. When constructing the source file structure, Handly does not prescribe any particular form of AST representation and in principle does not even require the presence of an AST itself, thus ensuring compatibility with almost any parsing mechanism. Finally, Handly supports full integration with the Eclipse workspace, but can also work directly with file systems thanks to its integration with
Current version
As noted above, one of these products is 1C:Enterprise Development Tools, where Handly is used from the very beginning to model elements of the high-level structure of such 1C:Enterprise languages as an embedded programming language and a query language. Another product is less known to the general public. This
We hope that after the release of version 1.0 with a guarantee of API stability and the exit of the project from the state of incubation, Handly will also have new adopters. In the meantime, the project continues to test and refine the API further, with two "big" releases per year, in June (the same date as the concurrent Eclipse release) and December, providing a predictable schedule that adopters can rely on. We can also add that the “bug rate” of the project remains at a consistently low level and Handly has been working reliably in the products of early adopters since the very first versions. For further familiarity with Eclipse Handly, you can use
Source: habr.com