This article aims to provide an introductory architectural overview of the Eclipse Platform and the other Eclipse components that form the foundation of 1C:Enterprise Development Tools (the new Designer). Of course we cannot go deep into details because the article is intended not only for Eclipse developers. However, we hope it could offer something of interest even for the more experienced Eclipse developers. For example, it unveils one of the "Eclipse secrets", a relatively new and not yet well-known project
Introduction to Eclipse architecture
First, let us talk about general aspects of Eclipse architecture using
First of all, Eclipse architecture features distinct layers where language-independent functionality stands apart from functionality that supports specific programming languages, and UI-independent core components stand apart from the components that provide user interface support.
Thus, the Eclipse Platform defines the general language-independent infrastructure, while Java development tools add a fully-functional Java IDE. Both Eclipse Platform and JDT consist of multiple components, where each component belongs either to the UI-independent core or to the UI layer (see fig. 1).
Fig. 1. Eclipse Platform and JDT
Major Eclipse Platform components include:
- Runtime—Defines the plug-in infrastructure. Eclipse architecture is module-based. Essentially, Eclipse is a collection of extensions and extension points.
- Workspace—Manages one or several projects. A project contains files and folders, which are mapped to files and directories in the file system.
- Standard Widget Toolkit (SWT)—Incorporates basic user interface controls that are integrated with the operating system.
- JFace—Provides a set of UI frameworks built over SWT.
- Workbench—Defines Eclipse UI paradigm: editors, views, and perspectives.
Note that the Eclipse Platform provides many other components for building integrated development tools, including Debug, Compare, Search, and Team. The JFace Text component is also worth mentioning. It provides a basis for building "smart" source code editors. Regretfully, even a brief overview of these higher-level components or any components of the UI layer would be beyond the scope of this article. Thus, the rest of this section will only describe the core components of Eclipse Platform and JDT.
The infrastructure of Eclipse plug-ins is based on
Virtually any Eclipse Platform-based integrated development environment uses Eclipse workspaces. Usually it is a workspace that stores the source code of applications being developed in the IDE. Workspace structure is mapped directly to the file system; it consists of projects that store files and folders. These projects, files, and folders are called workspace resources. The implementation of the Eclipse workspace serves as a file system cache, which significantly improves the performance of traversing the resource tree. In addition to that, workspace provides some supplemental services, including
The Core Resources component (org.eclipse.core.resources plug-in) provides support for workspaces and their resources. In particular, this component provides programmatic access to the workspace in terms of the resource model. Efficient access to this model requires a simple way to represent references to resources for clients. However, the object that stores the resource state within the model should be hidden from clients. If this requirement is not met, in scenarios where a file is deleted, the client would not release the object that is no longer available in the model (and this would cause other issues). Eclipse solves this problem using resource handles. A handle serves as a key (it only stores the path to a resource in the workspace) and it fully controls access to the object within the model that stores the resource state. This design is a variation of the
Fig. 2 illustrates the usage of the Handle/Body idiom in the resource model. The IResource interface represents a resource handle and serves as an API. The Resource class, which implements this interface, and the ResourceInfo class, which represents the body, are not APIs. Note that a handle stores only a resource path relative to the workspace root and does not store a reference to resource info. Resource info objects form a so-called "element tree". This data structure is fully stored in the memory. To find a resource info instance that matches a specific handle, the element tree is traversed according to the path stored in the handle.
Fig. 2. IResource and ResourceInfo
As we shall see later, the handle-based design of the resource model is also used for many other models available in Eclipse. This design has the following characteristics:
- Handle is a value object. Value objects are immutable objects whose equality is not based on identity. Such objects can be safely used as keys in hashed containers. Multiple handle instances can refer to a single resource. To compare them, use the equals(Object) method.
- A handle defines resource behavior but does not store resource state, it only stores the "key" (the resource path).
- A handle can refer to a resource that does not exist (the resource is not yet created or it is already deleted). You can use the IResource.exists() method to check whether a resource exists.
- The implementation of some operations can be based solely on the data stored in a handle (handle-only operations). Examples of handle-only operations are IResource.getParent() and getFullPath(). Such operations can be successfully executed even if the resource does not exist. If an operation requires an existing resource, it throws a CoreException if the resource is not found.
Eclipse provides a powerful mechanism for efficiently notifying clients of resource changes in the workspace (see fig. 3). Resources can be changed from within the Eclipse IDE and during the synchronization with the file system. In both cases clients subscribed to the notifications get the change descriptions in the form of resource deltas. A delta describes the difference between two states of a resource (sub)tree; it is itself a tree where each node stores a resource change description and a list of child deltas that describe changes in the subordinate resources.
Fig. 3. IResourceChangeEvent and IResourceDelta
Delta-based notifications have the following characteristics:
- A single change and a set of changes are described by a uniform structure (because deltas are composed recursively). Subscribed clients can process resource change notifications using recursive descent through a delta tree.
- A delta contains a full change description, including resource moves or changes in "markers" associated with the resource (such as compilation errors).
- Since references to resources are handle-based, a delta can naturally refer to a deleted resource.
As we shall see later, the main principles of the design of resource change notifications are also used in many other handle-based models in Eclipse.
The resource model of the Eclipse workspace is a fundamental language-independent model. The JDT Core component (org.eclipse.jdt.core plug-in) provides an API for navigating and analyzing the workspace structure from the point of view of the Java language (the so-called Java model). This API is defined in terms of Java elements, unlike the underlying resource model API, which is defined in terms of files and folders. Fig. 4 shows the main interfaces of the Java element tree.
Fig. 4. Java model elements
The Java model uses the same handle/body idiom as the resource model uses (see fig. 5). IJavaElement is a handle, JavaElementInfo is a body. IJavaElement defines the protocol that is common for all Java elements. Some of its methods are handle-only, such as getElementName() and getParent(). The JavaElementInfo object stores the element state, which includes element structure and attributes.
Fig. 5. IJavaElement and JavaElementInfo
Unlike the basic handle/body design of the resource model, the one used in the Java model is more involved. As it was mentioned before, in the resource model, the element tree, which consists of resource info objects, is fully stored in the memory. However, the Java model might include much more elements than a resource tree because it also stores the internal structure of .java and .class files: types, fields, and methods.
To avoid the need to store the entire element tree in the memory, the Java model implementation uses a bounded LRU cache of element info where IJavaElement handle serves as a key. Element info objects are created upon request during the navigation through the element tree. Rarely used elements are evicted from the cache, so the total amount of memory used by the model is limited by the cache size. This is another advantage of a handle-based design, which completely hides such implementation details from the client code.
Notifications about Java element changes are similar to notifications about workspace resource changes, which are described earlier in this article. To monitor the changes in the Java model, a client subscribes to notifications, where each notification is an ElementChangedEvent that contains IJavaElementDelta (see fig. 6).
Fig. 6. ElementChangedEvent and IJavaElementDelta
The Java model does not contain any information about method bodies or name lookup. This is why JDT Core provides an additional model for detailed analysis of Java code:
Since syntax trees can occupy large amounts of memory, JDT only caches a single AST that corresponds to the active editor. Unlike the Java model, the AST is usually treated as an "intermediate" and "temporary" model, thus, clients should not keep references to the elements of this model outside of the context of the operation that generated the AST.
The three listed models (Java model, AST, and bindings) provide a basis for building "smart development tools" in JDT, including the powerful Java editor with a lot of code assistants, source actions (such as "Orgainize Imports" and "Format"), and search and refactoring tools. Moreover, the Java model provides a basis for various "structure views", such as Package Explorer, Outline, Search, Call Hierarchy, and Type Hierarchy.
Eclipse components used in 1С:Enterprise Development Tools
Fig. 7 shows the Eclipse components that form a basis of the technological platform for 1C:Enterprise Development Tools.
Fig. 7. Eclipse as a platform for 1С:Enterprise Development Tools
The Eclipse Platform provides a basic infrastructure. We reviewed some of its aspects in the previous section.
Like any universal tool, EMF is suitable for solving a wide range of modeling tasks. However, some classes of models (such as the handle-based models described above) might require specialized modeling tools. Describing EMF in its entirety in a single article is clearly not practical; the subject is so extensive that it would easily fill a large volume. Let us just say that due to its high-quality universal design, EMF gave the origin to a variety of modeling projects. Together with EMF, these projects are a part of the top-level
1С:Enterprise Development Tools make active use of both EMF and other Eclipse Modeling projects. In particular, Xtext is a basis of the development tools for both 1C:Enterprise script and 1C:Enterprise query language. Another basis for these development tools is Eclipse Handly. We will tell more about it because it is the newest and, thus, lesser known Eclipse component among those mentioned in this article.
Earlier in this article we described the main architectural principles of handle-based models, such as the handle/body idiom, on the example of the resource model and the Java model. We also noted that both resource model and Java model are essential for Eclipse Java development tools (JDT). And since the architecture of almost all Eclipse *DT projects is similar to JDT architecture, it will not be a wild exaggeration to say that handle-based models serve as foundation for many, if not all, IDEs built over Eclipse Platform. For example, Eclipse C/C++ Development Tooling (CDT) includes a handle-based model for C/C++ that plays the same role as the Java model in JDT.
Before Handly, Eclipse did not offer any specialized libraries for building handle-based language models. The models that are available now were built mostly by direct adaptation of Java model’s code (a.k.a copy/paste), but only in projects where Eclipse Public License (EPL) allowed that. Obviously, this is not an issue for Eclipse projects; however, proprietary projects are a different matter. This method, in addition to its lack of consistency, is prone to other common problems, such as code duplication and bugs introduced during the adaptation. Even worse, the resulting models are self-contained entities, which do not use their unification potential. Meanwhile, definition of common concepts and protocols for handle-based language models could result in the development of reusable components for integration with these models, just like it was done in the case of EMF.
Of course, Eclipse developers were aware of these problems. Back in 2005,
The Handly project is aimed at solving tasks that are somewhat similar to those of EMF, but for handle-based models, and especially for language models (i.e. those that describe structure elements of programming languages). Handly was designed to address the following major goals:
- Definition of the major abstractions within the domain of handle-based models
- Reducing the effort required for the implementation of handle-based language models and increasing their quality due to code reuse
- Development of a uniform meta-level API for the resulting models, which opens the way to develop common IDE components that work with handle-based language models
- Flexibility and scalability
- Integration with Xtext (in a separate layer)
To define the common abstractions and protocols, we analyzed the existing implementations of handle-based language models. The main interfaces and base implementations provided by Handly are shown in fig. 8.
Fig. 8. Main interfaces and base implementations of Handly elements
The IElement interface represents an element handle and is a common interface for all Handly-based model elements. The abstract class Element implements a generalized handle/body mechanism (see fig. 9).
Fig. 9. IElement and generalized handle/body implementation
Besides, Handly provides a generalized facility for notifications about the changes of model elements (see fig. 10). As you can see, it is largely similar to notification mechanisms implemented in the resource model and in the Java model, and it uses IElementDelta for uniform representation of element changes.
Fig. 10. Common interfaces and base implementations of the notification facility in Handly
The part of Handly described above (see fig. 9 and fig. 10) is suitable for describing virtually any handle-based model. For language model development, the project provides additional functionality, namely common interfaces and base implementations for source text structure elements (the so-called source elements, see fig. 8 ). The ISourceFile interface represents a source file; the ISourceConstruct interface represents an element inside a source file. Abstract classes SourceFile and SourceConstruct implement generalized operations on source files and their elements, such as text buffer operations, binding to element position in the source text, and reconciling a model with the current content of working copy buffer. The implementation of these facilities is not a trivial task, and Handly can significantly reduce the model implementation effort due to the provided high-quality base implementations.
In addition to the main components described above, Handly provides the infrastructure for text buffers and snapshots, support of integration with source code editors (including the out-of-box integration with the Xtext editor), and some common UI components that can support Handly-based models, such as an outline framework. The project offers several demo examples, including a Handly-based implementation of the Java model. (Note that this implementation is intentionally simplified for clarity, compared to the full implementation of this model in JDT.)
As we mentioned before, since the beginning of Handly development we paid significant attention to scalability and flexibility, and we are still committed to this.
In general, handle-based models have decent scalability by design. For example, the handle/body idiom can limit the amount of memory used by the model; however, several fine points must be considered. For example, during the scalability testing of Handly we found an issue in the implementation of the notification mechanism: in scenarios with many changed elements, building the delta took too much time. We found that this issue is also present in the JDT Java model, which we used as a basis for our development. We fixed the issue in Handly and offered a patch for JDT, which was gladly accepted. It is just one of many examples showing the advantage of integration of Handly into existing models (because this would limit the scope of required fixes to one place).
To make the integration of Handly to existing model implementations possible, the library must provide significant flexibility. The major problem here is preserving backwards compatibility with the model API. This task was solved in
Flexibility also has other aspects. For example, Handly sets very few limitations for the model structure and therefore can be used for modeling both general-purpose languages and domain-specific languages. Handly does not require to use any specific AST representation for building the source file structure. It does not even require to use an AST at all, which ensures compatibility with virtually any parser. Finally, Handly supports fully-featured integration with Eclipse workspace, but is also capable of working with file systems directly due to integration with
The latest version of
As we mentioned earlier, one of these projects is 1C:Enterprise Development Tools. Since the very beginning, it uses Handly for modeling high-level structure elements of 1C:Enterprise script and 1C:Enterprise query language. The other project is less known to wide audience. It is
We hope that with the release of version 1.0 with stable API and the end of the incubation phase, Handly will acquire more adopters. Meanwhile, we test and improve the API and provide two releases each year: one in June (together with a new Eclipse release) and the other one in December. The adopters can rely on this schedule. It is also worth noting that the "bug rate" of the project always remains low and since the very first versions Handly has proved itself as a reliable tool to our early adopters. For more information about Eclipse Handly, please see