<commit_before># Developer uses projects from multiple Gradle builds from IDE

This feature allows a developer to work in a single IDE session on multiple projects that would normally be independent.

A typical workflow for a developer that has to work on 2 independent projects would be to make a change to project A, publish its artifact and build project B with the changed dependency. For developers this workflow is cumbersome and time-consuming. This feature allows a developer to work on multiple projects in a single IDE session that would normally be independent.

## 'Gradle Composite'

The defined stories introduce the concept of a ‘Gradle composite build’ to the tooling API. This is simply a collection of Gradle projects that the IDE user is working on. These projects may come from different Gradle builds.

A tooling API client will be able to define a composite and query it in a similar way to how a `ProjectConnection` can be queried. While the projects contained in a composite may come from separate Gradle builds, where possible the composite will present as if it were a single, unified Gradle build containing all of the projects for each participating Gradle build.

This will provide the developer with a view of all the projects in the composite, so that the developer can search for usages or make changes to any of these projects. When the developer compiles or runs tests from withing the IDE, these changes will be picked up for any dependent project. However, IDE actions that delegate to Gradle (such as task execution) will not operate on a composite build, as these actions will not (yet) be composite-aware.

In scope are changes to Buildship to define and use a composite build. Out of scope are changes to IDEA.

## Project substitution

Where possible, binary dependencies will be replaced with source dependencies between IDE modules.

So, for example, application A and library B might normally be built separately, as part of different builds. In this instance, application A would have a binary dependency on library B, consuming it as a jar downloaded from a binary repository. When application A and library B are both imported in the same composite, however, application A would have a source dependency on library B.

## Stories

### Story - Tooling API provides EclipseProject model for a composite containing a single Gradle build.

Introduce `EclipseWorkspace` to the Tooling API. This represents a collection of eclipse projects based on the Gradle builds that the IDE user is working on. For this story, all Gradle projects for an `EclipseWorkspace` will be sourced from a single Gradle build. As such, this story merely provides a convenience for obtaining a flattened collection of `EclipseProject` instances for a single Gradle build.

On completion of this story, it will be possible to convert Buildship to use this new API for project import and refresh, preparing for the next story which provides an `EclipseWorkspace` model for a composite of Gradle builds. Converting Buildship is the subject of the next story.

##### API

```
    abstract class GradleCompositeBuilder {
         static CompositeBuilder newComposite() { ... }
         CompositeBuilder withParticipant(ProjectConnection participant) { ... }
         CompositeBuild build(() { ... }
    }

    interface GradleConnection {
        // Extracted from ProjectConnection
        <T> T getModel(Class<T> modelType) throws GradleConnectionException, IllegalStateException;
        <T> void getModel(Class<T> modelType, ResultHandler<? super T> handler) throws IllegalStateException;
        <T> ModelBuilder<T> model(Class<T> modelType);
    }

    /**
     * EclipseWorkspace is not a supported model type.
     */
    interface ProjectConnection extends GradleConnection {
    }

    /**
     * For now, the only model type supported is EclipseWorkspace.
     */
    interface GradleComposite extends GradleConnection {
        // No other methods
    }

    interface EclipseWorkspace {
        /**
         * A flattened set of all projects in the Eclipse workspace.
         * These project models are fully configured, and may be expensive to calculate.
         * Note that not all projects necessarily share the same root.
         */
        Set<EclipseProject> getOpenProjects();
    }

    ProjectConnection connection = GradleConnector.newConnector().forProjectDirectory(new File("myProject")).connect();
    GradleComposite composite = GradleCompositeBuilder.newComposite().withParticipant(connection).build()
    EclipseWorkspace eclipseWorkspace = composite.model(EclipseWorkspace.class)
```

##### Implementation

- The `CompositeBuilder` provides a means to define a composite build via the Tooling API. Each `ProjectConnection` added to the composite specifies a Gradle build that participates in the composite.
    - Adding any `ProjectConnection` effectively adds the Gradle build that _contains_ the referenced project to the composite.
    - TODO: Should adding a `ProjectConnection` for a non-root project fail? Ideally, yes, but it may be expensive to calculate early.
    - For this story, adding multiple connections to the composite is not permitted.
- The only model type that can be requested for a `CompositeBuild` is `EclipseWorkspace`
    - On request for an `EclipseWorkspace`, the `ProjectConnection` will be queried for the `EclipseProject` model. This model represents the hierarchy of all eclipse projects for the Gradle build.
    - The instance of `EclipseWorkspace` will be constructed directly by the `CompositeBuild` instance, by traversing the hierarchy of the `EclipseProject` obtained.
    - the `EclipseProject`s still contain the information about their hierarchy, so Buildship can potentially display then in a hierarchical layout
    - A delegating implementation of `ModelBuilder` will be required

##### Test cases

TBD

### Story - Buildship queries `EclipseWorkspace` to determine set of Eclipse projects for an imported Gradle build

By switching to use the new `EclipseWorkspace` model from the Tooling API, Buildship (and tooling-commons) will no longer need to traverse the hierarchy of eclipse projects. This change will enable Buildship to later take advantage of project substitution and name de-duplication for composite builds.

##### API

The `ToolingClient` will be expanded to provide methods for managing and querying a composite.

##### Implementation

 The `ModelRepository` (the abstraction that Buildship works with) will use the new methods on the `ToolingClient`to ask for the `EclipseWorkspace` instead of asking for individual `EclipseProject`s. Buildship itself can remain completely unchanged as a result.

##### Test cases<commit_msg>Cleanup and test cases for first Tooling API story<commit_after># Developer uses projects from multiple Gradle builds from IDE

This feature allows a developer to work in a single IDE session on multiple projects that would normally be independent.

A typical workflow for a developer that has to work on 2 independent projects would be to make a change to project A, publish its artifact and build project B with the changed dependency. For developers this workflow is cumbersome and time-consuming. This feature allows a developer to work on multiple projects in a single IDE session that would normally be independent.

## 'Gradle Composite'

The defined stories introduce the concept of a ‘Gradle composite build’ to the tooling API. This is simply a collection of Gradle projects that the IDE user is working on. These projects may come from different Gradle builds.

A tooling API client will be able to define a composite and query it in a similar way to how a `ProjectConnection` can be queried. While the projects contained in a composite may come from separate Gradle builds, where possible the composite will present as if it were a single, unified Gradle build containing all of the projects for each participating Gradle build.

This will provide the developer with a view of all the projects in the composite, so that the developer can search for usages or make changes to any of these projects. When the developer compiles or runs tests from withing the IDE, these changes will be picked up for any dependent project. However, IDE actions that delegate to Gradle (such as task execution) will not operate on a composite build, as these actions will not (yet) be composite-aware.

In scope are changes to Buildship to define and use a composite build. Out of scope are changes to IDEA.

## Project substitution

Where possible, binary dependencies will be replaced with source dependencies between IDE modules.

So, for example, application A and library B might normally be built separately, as part of different builds. In this instance, application A would have a binary dependency on library B, consuming it as a jar downloaded from a binary repository. When application A and library B are both imported in the same composite, however, application A would have a source dependency on library B.

## Stories

### Story - Tooling API provides EclipseProject model for a composite containing a single Gradle build.

Introduce `EclipseWorkspace` to the Tooling API. This represents a collection of eclipse projects based on the Gradle builds that the IDE user is working on.
For this story, all Gradle projects for an `EclipseWorkspace` will be sourced from a single Gradle build. As such, this story merely provides a convenience for
obtaining a flattened collection of `EclipseProject` instances for a single Gradle build.

On completion of this story, it will be possible to convert Buildship to use this new API for project import and refresh, preparing for the next story which provides
an `EclipseWorkspace` model for a composite of Gradle builds. Converting Buildship is the subject of the next story.

##### API

```
    public abstract class GradleCompositeBuilder {
         public static GradleCompositeBuilder newComposite() { ... }
         protected abstract GradleCompositeBuilder withParticipant(ProjectConnection participant) { ... }
         protected abstract GradleComposite build() { ... }
    }

    interface GradleConnection {
        // Extracted from ProjectConnection
        <T> T getModel(Class<T> modelType) throws GradleConnectionException, IllegalStateException;
        <T> void getModel(Class<T> modelType, ResultHandler<? super T> handler) throws IllegalStateException;
        <T> ModelBuilder<T> model(Class<T> modelType);
    }

    /**
     * EclipseWorkspace is not a supported model type.
     */
    public interface ProjectConnection extends GradleConnection {
    }

    /**
     * For now, the only model type supported is EclipseWorkspace.
     */
    public interface GradleComposite extends GradleConnection {
        // No other methods
    }

    public interface EclipseWorkspace {
        /**
         * A flattened set of all projects in the Eclipse workspace.
         * These project models are fully configured, and may be expensive to calculate.
         * Note that not all projects necessarily share the same root.
         */
        Set<EclipseProject> getOpenProjects();
    }

    ProjectConnection connection = GradleConnector.newConnector().forProjectDirectory(new File("myProject")).connect();
    GradleComposite composite = GradleCompositeBuilder.newComposite().withParticipant(connection).build();
    EclipseWorkspace eclipseWorkspace = composite.model(EclipseWorkspace.class);
```

##### Implementation

- The `CompositeBuilder` provides a means to define a composite build via the Tooling API. Each `ProjectConnection` added to the composite specifies a Gradle build that
participates in the composite.
    - Adding any `ProjectConnection` effectively adds the Gradle build that _contains_ the referenced project to the composite.
    - TODO: Should adding a `ProjectConnection` for a non-root project fail? Ideally, yes, but it may be expensive to calculate early.
    - For this story, adding multiple connections to the composite is not permitted.
- The only model type that can be requested for a `GradleComposite` is `EclipseWorkspace`
    - On request for an `EclipseWorkspace`, the `ProjectConnection` will be queried for the `EclipseProject` model. This model represents the hierarchy of all eclipse projects
    for the Gradle build.
    - The instance of `EclipseWorkspace` will be constructed directly by the `GradleComposite` instance, by traversing the hierarchy of the `EclipseProject` obtained.
    - the `EclipseProject`s still contain the information about their hierarchy, so Buildship can potentially display them in a hierarchical layout
    - A delegating implementation of `ModelBuilder` will be required

##### Test cases

- A composite cannot be built without assigning at least one participating project. If the `GradleCompositeBuilder.build()` method is called without assigning at least one participant
an `IllegalStateException` is thrown.
- A composite can only add a single participating project via `GradleCompositeBuilder.withParticipant()`. If `GradleCompositeBuilder.withParticipant()` is called twice, a `IllegalStateException` is thrown.
- Requesting a model by calling any of the methods in `GradleConnection` with a type that's not an interface will throw a `IllegalArgumentException`.
- A model can only be request for the type `EclipseWorkspace`. Providing any other type throws a `IllegalArgumentException`.
- A composite can be built with a single participating project. The hierarchy of the participating project only contains a single Gradle project. The requested `EclipseWorkspace` model
contains a single project of type `EclipseProject`. The `EclipseProject` properly populates the model (e.g. name, path, classpath and project dependencies).
- A composite can be built with a single participating project containing a hierarchy of Gradle projects. The requested `EclipseWorkspace` model
contains all projects of the hierarchy (including the root project) with type `EclipseProject`. The `EclipseProject` properly populates the model
(e.g. name, path, classpath and project dependencies).
- If the `ProjectConnection` points to a subproject of a multi-project build hierarchy, the requested `EclipseWorkspace` model determines the root project and traverses the whole hierarchy.
The `EclipseWorkspace` contains all `EclipseProject` of that hierarchy.

##### Open issues

- Dealing with `ProjectConnection` is a heavy-weight approach and requires loading the whole model for all projects involved. A light-weight approach needs to be introduced (a project identifier)
that allows for determining a project and its path without loading up the model.

### Story - Buildship queries `EclipseWorkspace` to determine set of Eclipse projects for an imported Gradle build

By switching to use the new `EclipseWorkspace` model from the Tooling API, Buildship (and tooling-commons) will no longer need to traverse the hierarchy of eclipse projects. This change will enable Buildship to later take advantage of project substitution and name de-duplication for composite builds.

##### API

The `ToolingClient` will be expanded to provide methods for managing and querying a composite.

##### Implementation

 The `ModelRepository` (the abstraction that Buildship works with) will use the new methods on the `ToolingClient`to ask for the `EclipseWorkspace` instead of asking for individual `EclipseProject`s. Buildship itself can remain completely unchanged as a result.

##### Test cases
{'fraction_non_alphanumeric': 0.04830824649427506, 'fraction_numerical': 0.00012865045670912132, 'mean_word_length': 4.344448264008251, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4264783', 'n_tokens_mistral': 3567, 'n_tokens_neox': 3456, 'n_words': 2049}