Skip to content

Commit

Permalink
Applied common Spotless Eclipse framework to JDT (#239)
Browse files Browse the repository at this point in the history
Applied common Spotless Eclipse framework to JDT (see #234).
Updated version and artifact ID as discussed in #226.
  • Loading branch information
fvgh committed Jul 18, 2018
1 parent 263a03f commit 7cdfcb4
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 192 deletions.
6 changes: 4 additions & 2 deletions _ext/eclipse-jdt/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# spotless-eclipse
# spotless-eclipse-jdt

Eclipse is not available in a form which can be easily consumed by maven or gradle. To fix this, we publish Eclipse's formatter and all its dependencies, along with a small amount of glue code, into the `com.diffplug.gradle.spotless:spotless-eclipse` artifact.
Eclipse JDT and its dependencies require a large amount of byte code.
Hence they should not be directly be required by the Spotless, but only be requested in case
they are configured by the Spotless configuration. Hence we publish Eclipse's formatter and all its dependencies, along with a small amount of glue code, into the `com.diffplug.gradle.spotless:spotless-eclipse-jdt` artifact.

To publish a new version, update the `_ext/eclipse-jdt/gradle.properties` appropriately and run this from the root directory:

Expand Down
188 changes: 13 additions & 175 deletions _ext/eclipse-jdt/build.gradle
Original file line number Diff line number Diff line change
@@ -1,180 +1,18 @@
plugins {
// bintray uploading
id 'com.jfrog.bintray' version '1.3.1'
// p2 dependencies
id 'com.diffplug.gradle.p2.asmaven' version '3.9.0'
}

apply plugin: 'java'

// The dependencies we'd like to pull from Eclipse's p2 repositories
def eclipseDeps = [
// The dependencies we actually use
'org.eclipse.jdt.core',
'org.eclipse.text',

// Their transitives
'org.eclipse.core.contenttype',
'org.eclipse.core.jobs',
'org.eclipse.core.runtime',
'org.eclipse.core.resources',
'org.eclipse.equinox.common',
'org.eclipse.equinox.preferences',
'org.eclipse.osgi'
]
// build a maven repo in our build folder containing these artifacts
p2AsMaven {
group 'p2', {
repoEclipse jdt_VER_ECLIPSE
eclipseDeps.each { p2.addIU(it) }
eclipseDeps.each { p2.addIU(it + '.source') }
}
}
apply from: rootProject.file('../gradle/java-setup.gradle')
apply from: rootProject.file('../gradle/java-publish.gradle')

configurations {
embeddedJars // we will embed these into the binary artifact
compile.extendsFrom(embeddedJars)
ext {
developers = [
fvgh: [ name: 'Frank Vennemeyer', email: 'frankgh@zoho.com' ],
nedtwigg: [ name: 'Ned Twigg', email: 'ned.twigg@diffplug.com' ],
]
}

dependencies {
// add the eclipse jars to the embedded configuration
eclipseDeps.each { embeddedJars "p2:${it}:+" }
}

jar {
// this embeds the eclipse jars into our "fat jar"
from {
configurations.embeddedJars.collect{ it.isDirectory() ? it : zipTree(it) }
compile "com.diffplug.spotless:spotless-eclipse-base:${VER_SPOTLESS_ECLISPE_BASE}"
compile("org.eclipse.jdt:org.eclipse.jdt.core:${VER_ECLIPSE_JDT_CORE}") {
exclude group: 'org.eclipse.platform', module: 'org.eclipse.ant.core'
exclude group: 'org.eclipse.platform', module: 'org.eclipse.core.expressions'
exclude group: 'org.eclipse.platform', module: 'org.eclipse.core.filesystem'
}
// the eclipse jars are signed, and our fat jar breaks the signatures
// so we've gotta be sure to filter out the signatures
exclude 'META-INF/*.RSA'
exclude 'META-INF/*.SF'
}

apply plugin: 'eclipse'
eclipse {
classpath {
downloadSources true
downloadJavadoc true
}
}
// always create fresh projects
tasks.eclipse.dependsOn(cleanEclipse)

///////////
// MAVEN //
///////////
apply plugin: 'maven-publish'

task sourcesJar(type: Jar) {
classifier = 'sources'
from sourceSets.main.allJava
}

task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}

////////////////
// PUBLISHING //
////////////////
def isSnapshot = project.jdt_version.endsWith('-SNAPSHOT')
// pulls the credentials from either the environment variable or gradle.properties
def cred = {
if (System.env[it] != null) {
return System.env[it]
} else if (project.hasProperty(it)) {
return project[it]
} else {
return 'unknown_' + it
}
}

model {
publishing {
publications {
mavenJava(MavenPublication) {
artifact jar
artifact sourcesJar
artifact javadocJar

groupId project.jdt_group
artifactId project.jdt_artifactId
version project.jdt_version

pom.withXml {
// remove the p2 dependencies because they are embedded
// also remove 'com.diffplug' dependencies so that we can use spotless on its dependencies
asNode().dependencies.'*'.each() {
if (it.groupId.text() == 'p2') {
it.parent().remove(it)
}
}
// add MavenCentral requirements to the POM
asNode().children().last() + {
resolveStrategy = Closure.DELEGATE_FIRST
name project.jdt_artifactId
description project.jdt_description
url "https://github.com/${project.jdt_org}/${project.name}"
scm {
url "https://github.com/${project.jdt_org}/${project.name}"
connection "scm:git:git://github.com/${project.jdt_org}/${project.name}"
developerConnection "scm:git:ssh:git@github.com/${project.jdt_org}/${project.name}"
}
licenses {
license {
name 'Eclipse Public License - v 1.0'
url 'https://www.eclipse.org/legal/epl-v10.html'
distribution 'repo'
}
}
developers {
developer {
id 'nedtwigg'
name 'Ned Twigg'
email 'ned.twigg@diffplug.com'
}
}
}
}
}
}
if (isSnapshot) {
// upload snapshots to oss.sonatype.org
repositories { maven {
url = 'https://oss.sonatype.org/content/repositories/snapshots'
credentials {
username = cred('nexus_user')
password = cred('nexus_pass')
}
} }
}
}
}

if (!isSnapshot) {
// upload releases to bintray and then mavenCentral
bintray {
user = cred('bintray_user')
key = cred('bintray_pass')
publications = ['mavenJava']
publish = true
pkg {
repo = 'opensource'
name = project.jdt_artifactId
userOrg = project.jdt_org
version {
name = project.jdt_version
mavenCentralSync {
user = cred('nexus_user')
password = cred('nexus_pass')
}
}
}
}

publish.dependsOn(bintrayUpload)
bintrayUpload.dependsOn(['generatePomFileForMavenJavaPublication', jar, sourcesJar, javadocJar])
}
}
18 changes: 10 additions & 8 deletions _ext/eclipse-jdt/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# jdt_ to ensure gradle.properties in project root doesn't clobber
jdt_version=4.7.2
jdt_artifactId=spotless-ext-eclipse-jdt
jdt_description=Eclipse's formatter bundled for Spotless
# Mayor/Minor versions correspond to the minimum Eclipse version supported/tested.
# Patch version is incremented for backward compatible patches of this library.
ext_version=4.8.0
ext_artifactId=spotless-eclipse-jdt
ext_description=Eclipse's JDT formatter bundled for Spotless

jdt_org=diffplug
jdt_group=com.diffplug.spotless
ext_org=diffplug
ext_group=com.diffplug.spotless

# Build requirements
jdt_VER_JAVA=1.8
ext_VER_JAVA=1.8

# Compile
jdt_VER_ECLIPSE=4.7.2
VER_ECLIPSE_JDT_CORE=[3.12.0,4.0.0[
VER_SPOTLESS_ECLISPE_BASE=[3.0.0,4.0.0[
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,35 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.gradle.spotless.java.eclipse;
package com.diffplug.spotless.extra.eclipse.java;

import java.util.Properties;

import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.text.edits.TextEdit;

/** Formatter step which calls out to the Eclipse formatter. */
public class EclipseFormatterStepImpl {
import com.diffplug.spotless.extra.eclipse.base.SpotlessEclipseFramework;

/** Formatter step which calls out to the Eclipse JDT formatter. */
public class EclipseJdtFormatterStepImpl {

private final CodeFormatter codeFormatter;

public EclipseFormatterStepImpl(Properties settings) {
public EclipseJdtFormatterStepImpl(Properties settings) throws Exception {
SpotlessEclipseFramework.setup(
plugins -> {
plugins.applyDefault();
plugins.add(new JavaCore());
});
this.codeFormatter = ToolFactory.createCodeFormatter(settings, ToolFactory.M_FORMAT_EXISTING);
}

private static final String UNIX = "\n";

public String format(String raw) throws Exception {
TextEdit edit = codeFormatter.format(CodeFormatter.K_COMPILATION_UNIT | CodeFormatter.F_INCLUDE_COMMENTS, raw, 0, raw.length(), 0, UNIX);
TextEdit edit = codeFormatter.format(CodeFormatter.K_COMPILATION_UNIT | CodeFormatter.F_INCLUDE_COMMENTS, raw, 0, raw.length(), 0, SpotlessEclipseFramework.LINE_DELIMITER);
if (edit == null) {
throw new IllegalArgumentException("Invalid java syntax for formatting.");
} else {
Expand All @@ -44,4 +50,5 @@ public String format(String raw) throws Exception {
return doc.get();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2016 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/** Eclipse JDT based Spotless formatter */
@ParametersAreNonnullByDefault
package com.diffplug.spotless.extra.eclipse.java;

import javax.annotation.ParametersAreNonnullByDefault;
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright 2016 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.spotless.extra.eclipse.java;

import static com.diffplug.spotless.extra.eclipse.base.SpotlessEclipseFramework.LINE_DELIMITER;
import static org.junit.Assert.*;

import java.util.Properties;
import java.util.function.Consumer;

import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
import org.junit.Test;

/** Eclipse JDT wrapper integration tests */
public class EclipseJdtFormatterStepImplTest {

private final static String UNFORMATTED = "package com.diffplug.gradle.spotless;\n" +
"public class C {\n" +
" static void hello() {" +
" System.out.println(\"Hello World!\");\n" +
" }\n" +
"}".replaceAll("\n", LINE_DELIMITER);
private final static String FORMATTED = "package com.diffplug.gradle.spotless;\n" +
"public class C {\n" +
"\tstatic void hello() {\n" +
"\t\tSystem.out.println(\"Hello World!\");\n" +
"\t}\n" +
"}".replaceAll("\n", LINE_DELIMITER);

private final static String PRE_UNFORMATTED = "/**<pre>void f(){}</pre>*/\n".replaceAll("\n", LINE_DELIMITER);
private final static String PRE_FORMATTED = "/**\n * <pre>\n * void f() {\n * }\n * </pre>\n */\n".replaceAll("\n", LINE_DELIMITER);

private final static String ILLEGAL_CHAR = Character.toString((char) 254);

@Test
public void defaultFormat() throws Throwable {
String output = format(UNFORMATTED, config -> {});
assertEquals("Unexpected formatting with default preferences.",
FORMATTED, output);
}

@Test
public void invalidFormat() throws Throwable {
String output = format(FORMATTED.replace("void hello() {", "void hello() "), config -> {});
assertTrue("Incomplete Java not formatted on best effort basis.", output.contains("void hello() " + LINE_DELIMITER));
}

@Test
public void invalidCharater() throws Throwable {
String output = format(FORMATTED.replace("void hello() {", "void hello()" + ILLEGAL_CHAR + " {"), config -> {});
assertTrue("Invalid charater not formatted on best effort basis.", output.contains("void hello()" + ILLEGAL_CHAR + " {" + LINE_DELIMITER));
}

@Test
public void invalidConfiguration() throws Throwable {
String output = format(FORMATTED, config -> {
config.setProperty(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, JavaCore.SPACE);
config.setProperty(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, "noInteger");
});
assertEquals("Invalid indentation configuration not replaced by default value (4 spaces)",
FORMATTED.replace("\t", " "), output);
}

@Test
/** Test that an internal code formatter can be created to format the Java code within HTML pre-tags. (see also Spotless issue #191) */
public void internalCodeFormatter() throws Throwable {
String output = format(PRE_UNFORMATTED + UNFORMATTED, config -> {
config.setProperty(
DefaultCodeFormatterConstants.FORMATTER_COMMENT_FORMAT_HEADER,
DefaultCodeFormatterConstants.TRUE);
});
assertEquals("Code within HTML <pre> tag not formatted.",
PRE_FORMATTED + FORMATTED, output);
}

private static String format(final String input, final Consumer<Properties> config) throws Exception {
Properties properties = new Properties();
config.accept(properties);
EclipseJdtFormatterStepImpl formatter = new EclipseJdtFormatterStepImpl(properties);
return formatter.format(input);
}

}

0 comments on commit 7cdfcb4

Please sign in to comment.