Skip to content

Spotless Eclipse Framework Final #261

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jul 18, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Enhanced framework interfaces.
Eclipse core uses plugins not derived from Plugin but BundleActivator.
Provided possibility to configure global preferences.
Usage of Consumer allows the user to get rid of statics.
  • Loading branch information
fvgh committed Apr 17, 2018
commit 5b63850f6b4a4b0ce55808478c2ef3fe42d3aaaf
23 changes: 7 additions & 16 deletions _ext/eclipse-base/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,16 @@ dependencies {
In the constructor of your formatter, the Spotless Eclipse Framework can be configured depending on your formatters requirements. For example the JDT formatter can be configured like:

```Java
//Eclipse plugins can be statically created, since their constructors
//do not require a running Eclipse environment.
private final static Plugin[] PLUGINS = {
SpotlessEclipseFramework.DefaultPlugins.RESOURCES.create(),
new org.eclipse.jdt.core.JavaCore()
};

public EclipseFormatterStepImpl(Properties settings) throws Exception {
SpotlessEclipseFramework.setup(PLUGINS, config -> {
config.disableDebugging();
config.hideEnvironment();
config.ignoreContentType();
config.ignoreUnsupportedPreferences();
config.useTemporaryLocations();
});
public EclipseFormatterStepImpl(Properties settings) throws Exception {
SpotlessEclipseFramework.setup(plugins -> {
plugins.addAll(SpotlessEclipseFramework.DefaultPlugins.createAll());
plugins.add(new org.eclipse.jdt.core.JavaCore());
});
...
```

The framework also supports fat JARs. In this cases the resources required by plugins, especially the `META-INF` and plugin information, must be located in locations unique
The framework also supports fat JARs, providing multiple plugins.
In this cases the resources required by plugins, especially the `META-INF` and plugin information, must be located in locations unique
to the plugin.
For this purpose the framework expects that these resources are stored in a sub-directory
which has the name of the package containing the plugin. For example in case the JDT plugin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Collection;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.xml.parsers.SAXParserFactory;

import org.eclipse.core.runtime.Plugin;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleException;
Expand All @@ -36,34 +36,12 @@
/** Setup a framework for Spotless Eclipse based formatters */
public final class SpotlessEclipseFramework {

/** Default plugins required by most Spotless formatters */
public enum DefaultPlugins {
/**
* The resources plugin initialized the Eclipse workspace and allows URL look-up.
* Most formatters using the workspace to resolve URLs or create
* file interfaces (org.eclipse.core.resources.IFile).
*/
RESOURCES(org.eclipse.core.resources.ResourcesPlugin.class);

private final Class<? extends Plugin> pluginClass;

DefaultPlugins(Class<? extends Plugin> clazz) {
pluginClass = clazz;
}

/** Create new plugin instance. */
public Plugin create() {
return createInstance(pluginClass);
}

/** Create plugin instances for all enumerated values. */
public static Plugin[] createValues() {
List<Plugin> instances = Arrays.stream(values()).map(value -> value.create()).collect(Collectors.toList());
return instances.toArray(new Plugin[0]);
}
}

/** Default internal bundles/services required by most plugins */
/**
* Default internal bundles required by most plugins.
* <p>
* Services provided by an internal bundle are not accessed via the plugin registry, but by static methods.
* </p>
*/
public enum DefaultBundles {
/** Plugins ask the platform whether core runtime bundle is in debug mode. Note that the bundle requires the EnvironmentInfo service. */
PLATFORM(org.eclipse.core.internal.runtime.PlatformActivator.class),
Expand All @@ -86,9 +64,42 @@ public BundleActivator create() {
}

/** Create bundle activator instances for all enumerated values. */
public static BundleActivator[] createValues() {
List<BundleActivator> instances = Arrays.stream(values()).map(value -> value.create()).collect(Collectors.toList());
return instances.toArray(new BundleActivator[0]);
public static Collection<BundleActivator> createAll() {
return Arrays.stream(values()).map(value -> value.create()).collect(Collectors.toList());
}
}

/**
* Default plugins required by most Spotless formatters.
* <p>
* Eclipse plugins are OSGI bundles itself and do not necessarily derive from the Eclipse Plugin class.
* BundleActivator implementation may as well server as plugins.
* All plugins must provide a MANIFEST.MF, plugin.properties and plugin.xml description file,
* required for the plugin registration.
* </p>
*/
public enum DefaultPlugins {
/**
* The resources plugin initialized the Eclipse workspace and allows URL look-up.
* Most formatters using the workspace to resolve URLs or create
* file interfaces (org.eclipse.core.resources.IFile).
*/
RESOURCES(org.eclipse.core.resources.ResourcesPlugin.class);

private final Class<? extends BundleActivator> pluginClass;

DefaultPlugins(Class<? extends BundleActivator> clazz) {
pluginClass = clazz;
}

/** Create new plugin instance. */
public BundleActivator create() {
return createInstance(pluginClass);
}

/** Create plugin instances for all enumerated values. */
public static Collection<BundleActivator> createAll() {
return Arrays.stream(values()).map(value -> value.create()).collect(Collectors.toList());
}
}

Expand All @@ -104,71 +115,71 @@ private static <T> T createInstance(Class<? extends T> clazz) {
private static SpotlessEclipseFramework INSTANCE = null;

/**
* Creates and configures a new SpotlessEclipseFramework using DefaultBundles, DefaultPlugins and default services.
* Creates and configures a new SpotlessEclipseFramework using DefaultBundles, DefaultPlugins and default configuration.
* If there is already a an instance, the call is ignored.
* @return False if the SpotlessEclipseFramework instance already exists, true otherwise.
* @throws BundleException Throws exception in case the setup failed.
*/
public synchronized static boolean setup() throws BundleException {
return setup(config -> {
config.disableDebugging();
config.hideEnvironment();
config.ignoreContentType();
config.ignoreUnsupportedPreferences();
config.useTemporaryLocations();
});
return setup(plugins -> plugins.addAll(DefaultPlugins.createAll()));
}

/**
* Creates and configures a new SpotlessEclipseFramework using DefaultBundles and DefaultPlugins.
* If there is already a an instance, the call is ignored.
* @param config Framework service configuration
* @param plugins Eclipse plugins (which are also OSGi bundles) to start
* @return False if the SpotlessEclipseFramework instance already exists, true otherwise.
* @throws BundleException Throws exception in case the setup failed.
*/
public synchronized static boolean setup(final Consumer<SpotlessEclipseServiceConfig> config) throws BundleException {
return setup(DefaultPlugins.createValues(), config);
public synchronized static boolean setup(final Consumer<Collection<BundleActivator>> plugins) throws BundleException {
return setup(config -> config.applyDefault(), plugins);
}

/**
* Creates and configures a new SpotlessEclipseFramework using DefaultBundles.
* If there is already a an instance, the call is ignored.
* @param plugins Eclipse plugins (which are also OSGi bundles) to start
* @param config Framework service configuration
* @param plugins Eclipse plugins (which are also OSGi bundles) to start
* @return False if the SpotlessEclipseFramework instance already exists, true otherwise.
* @throws BundleException Throws exception in case the setup failed.
*/
public synchronized static boolean setup(Plugin[] plugins, final Consumer<SpotlessEclipseServiceConfig> config) throws BundleException {
return setup(DefaultBundles.createValues(), plugins, config);
public synchronized static boolean setup(Consumer<SpotlessEclipseServiceConfig> config, Consumer<Collection<BundleActivator>> plugins) throws BundleException {
return setup(bundles -> bundles.addAll(DefaultBundles.createAll()), config, plugins);
}

/**
* Creates and configures a new SpotlessEclipseFramework if there is none.
* If there is already a an instance, the call is ignored.
* @param bundleActivators Activators of internal bundles
* @param plugins Eclipse plugins (which are also OSGi bundles) to start
* @param bundles Activators of internal bundles
* @param config Framework service configuration
* @param plugins Eclipse plugins to start
* @return False if the SpotlessEclipseFramework instance already exists, true otherwise.
* @throws BundleException Throws exception in case the setup failed.
*/
public synchronized static boolean setup(BundleActivator[] bundleActivators, Plugin[] plugins, final Consumer<SpotlessEclipseServiceConfig> config) throws BundleException {
public synchronized static boolean setup(Consumer<Collection<BundleActivator>> bundles, Consumer<SpotlessEclipseServiceConfig> config, Consumer<Collection<BundleActivator>> plugins) throws BundleException {
if (null != INSTANCE) {
return false;
}
INSTANCE = new SpotlessEclipseFramework(bundleActivators);

Collection<BundleActivator> internalBundleActivators = new ArrayList<BundleActivator>();
bundles.accept(internalBundleActivators);
INSTANCE = new SpotlessEclipseFramework(internalBundleActivators);
config.accept(INSTANCE.getServiceConfig());
for (Plugin plugin : plugins) {

Collection<BundleActivator> pluginsList = new ArrayList<BundleActivator>();
plugins.accept(pluginsList);
for (BundleActivator plugin : pluginsList) {
INSTANCE.addPlugin(plugin);
}
return true;
}

private final Function<Bundle, BundleException> registry;
private final BundleController controller;
private final BundleActivator[] bundleActivators;
private final Collection<BundleActivator> bundleActivators;
private boolean bundleActivatorsStarted;

private SpotlessEclipseFramework(BundleActivator[] bundleActivators) throws BundleException {
private SpotlessEclipseFramework(Collection<BundleActivator> bundleActivators) throws BundleException {

controller = new BundleController();
registry = (pluginBundle) -> {
Expand All @@ -185,7 +196,7 @@ private SpotlessEclipseServiceConfig getServiceConfig() {
}

/** Add a plugin to the framework. */
private void addPlugin(Plugin plugin) throws BundleException {
private void addPlugin(BundleActivator plugin) throws BundleException {
if (!bundleActivatorsStarted) {
//The SAXParserFactory.class is required for parsing the plugin XML files
addMandatoryServiceIfMissing(SAXParserFactory.class, SAXParserFactory.newInstance());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package com.diffplug.gradle.spotless.eclipse;

import java.util.Map;

import org.eclipse.core.runtime.content.IContentTypeManager;
import org.eclipse.core.runtime.preferences.IPreferencesService;
import org.eclipse.osgi.service.datalocation.Location;
Expand All @@ -25,15 +27,21 @@

/**
* Configuration/Provision of services which shall be provided by the SpotlessEclipseFramework.
* <br>
* <p>
* The SpotlessEclipseFramework plugins are customized by these services to provide the minimal
* functionality as required by the Spotless formatter.
* <p>
* Note that the configuration must not be changed after adding plugins, since a concurrent
* access is not supported.
* </p>
*/
public interface SpotlessEclipseServiceConfig {

/** Sets property/preference value available to all bundles, plugins and services. */
void set(String key, String value);

/** Sets property/preference values available to all bundles, plugins and services. */
default void set(Map<String, String> properties) {
properties.entrySet().stream().forEach(x -> set(x.getKey(), x.getValue()));
}

/**
* Add custom service to collection.
* <p>
Expand Down Expand Up @@ -82,4 +90,12 @@ default public void useTemporaryLocations() {
add(Location.class, new TemporaryLocation());
}

/** Applies the default configurations. */
default public void applyDefault() {
hideEnvironment();
ignoreContentType();
disableDebugging();
ignoreUnsupportedPreferences();
useTemporaryLocations();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ public ServiceCollection(Bundle systemBundle, Map<String, String> properties) {
this.properties = properties;
}

@Override
public void set(String key, String value) {
properties.put(key, value);
}

@Override
public <S> void add(Class<S> interfaceClass, S service) {
String className = interfaceClass.getName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
* Installation related methods (update/uninstall) are unsupported.
* Unsupported methods are marked as deprecated and causing an exception.
*/
interface StaticBundle extends Bundle {
public interface StaticBundle extends Bundle {

@Override
default public void start(int options) throws BundleException {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
* Installation related methods (update/uninstall) are not supported.
* Unsupported methods are marked as deprecated and causing an exception.
*/
interface TemporaryBundle extends Bundle {
public interface TemporaryBundle extends Bundle {

@Override
default public Version getVersion() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import org.junit.Test;
import org.osgi.framework.BundleException;

/** Smoke tests (no mocking) */
/** Smoke tests */
public class SpotlessEclipseFrameworkTest {

@Test
Expand Down