Skip to content

Commit 90ae28d

Browse files
authored
Format Java source files automatically (#46745)
This commit adds a Java source formatter and checker into the build process. This is not yet enabled for any sub-projects - to format and check a sub-project, add its Gradle path into `build.gradle` and run: ./gradlew spotlessApply to format, and: ./gradlew spotlessJavaCheck # or: ./gradlew precommit to verify formatting.
1 parent cdd2d58 commit 90ae28d

File tree

10 files changed

+488
-40
lines changed

10 files changed

+488
-40
lines changed

.eclipseformat.xml

Lines changed: 362 additions & 0 deletions
Large diffs are not rendered by default.

CONTRIBUTING.md

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -155,19 +155,68 @@ For Eclipse, go to `Preferences->Java->Installed JREs` and add `-ea` to
155155

156156
### Java Language Formatting Guidelines
157157

158+
Java files in the Elasticsearch codebase are formatted with the Eclipse JDT
159+
formatter, using the [Spotless
160+
Gradle](https://github.com/diffplug/spotless/tree/master/plugin-gradle)
161+
plugin. This plugin is configured on a project-by-project basis, via
162+
`build.gradle` in the root of the repository. So long as at least one
163+
project is configured, the formatting check can be run explicitly with:
164+
165+
./gradlew spotlessJavaCheck
166+
167+
The code can be formatted with:
168+
169+
./gradlew spotlessApply
170+
171+
These tasks can also be run for specific subprojects, e.g.
172+
173+
./gradlew server:spotlessJavaCheck
174+
158175
Please follow these formatting guidelines:
159176

160177
* Java indent is 4 spaces
161178
* Line width is 140 characters
162-
* Lines of code surrounded by `// tag` and `// end` comments are included in the
163-
documentation and should only be 76 characters wide not counting
164-
leading indentation
165-
* The rest is left to Java coding standards
166-
* Disable “auto-format on save” to prevent unnecessary format changes. This makes reviews much harder as it generates unnecessary formatting changes. If your IDE supports formatting only modified chunks that is fine to do.
167-
* Wildcard imports (`import foo.bar.baz.*`) are forbidden and will cause the build to fail. This can be done automatically by your IDE:
168-
* Eclipse: `Preferences->Java->Code Style->Organize Imports`. There are two boxes labeled "`Number of (static )? imports needed for .*`". Set their values to 99999 or some other absurdly high value.
169-
* IntelliJ: `Preferences/Settings->Editor->Code Style->Java->Imports`. There are two configuration options: `Class count to use import with '*'` and `Names count to use static import with '*'`. Set their values to 99999 or some other absurdly high value.
170-
* Don't worry too much about import order. Try not to change it but don't worry about fighting your IDE to stop it from doing so.
179+
* Lines of code surrounded by `// tag` and `// end` comments are included
180+
in the documentation and should only be 76 characters wide not counting
181+
leading indentation
182+
* Wildcard imports (`import foo.bar.baz.*`) are forbidden and will cause
183+
the build to fail. This can be done automatically by your IDE:
184+
* Eclipse: `Preferences->Java->Code Style->Organize Imports`. There are
185+
two boxes labeled "`Number of (static )? imports needed for .*`". Set
186+
their values to 99999 or some other absurdly high value.
187+
* IntelliJ: `Preferences/Settings->Editor->Code Style->Java->Imports`.
188+
There are two configuration options: `Class count to use import with
189+
'*'` and `Names count to use static import with '*'`. Set their values
190+
to 99999 or some other absurdly high value.
191+
192+
#### Editor / IDE Support
193+
194+
Eclipse IDEs can import the file [elasticsearch.eclipseformat.xml]
195+
directly.
196+
197+
IntelliJ IDEs can
198+
[import](https://blog.jetbrains.com/idea/2014/01/intellij-idea-13-importing-code-formatter-settings-from-eclipse/)
199+
the same settings file, and / or use the [Eclipse Code
200+
Formatter](https://plugins.jetbrains.com/plugin/6546-eclipse-code-formatter)
201+
plugin.
202+
203+
You can also tell Spotless to [format a specific
204+
file](https://github.com/diffplug/spotless/tree/master/plugin-gradle#can-i-apply-spotless-to-specific-files)
205+
from the command line.
206+
207+
#### Formatting failures
208+
209+
Sometimes Spotless will report a "misbehaving rule which can't make up its
210+
mind" and will recommend enabling the `paddedCell()` setting. If you
211+
enabled this settings and run the format check again,
212+
Spotless will write files to
213+
`$PROJECT/build/spotless-diagnose-java/` to aid diagnosis. It writes
214+
different copies of the formatted files, so that you can see how they
215+
differ and infer what is the problem.
216+
217+
The `paddedCell() option is disabled for normal operation in order to
218+
detect any misbehaviour. You can enabled the option from the command line
219+
by running Gradle with `-Dspotless.paddedcell`.
171220

172221
### License Headers
173222

build.gradle

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ plugins {
3535
id 'com.gradle.build-scan' version '2.4'
3636
id 'lifecycle-base'
3737
id 'elasticsearch.global-build-info'
38+
id "com.diffplug.gradle.spotless" version "3.24.2" apply false
3839
}
3940

4041
apply plugin: 'nebula.info-scm'
@@ -98,6 +99,34 @@ subprojects {
9899
plugins.withType(BuildPlugin).whenPluginAdded {
99100
project.licenseFile = project.rootProject.file('licenses/APACHE-LICENSE-2.0.txt')
100101
project.noticeFile = project.rootProject.file('NOTICE.txt')
102+
103+
// Projects that should be formatted and checked with Spotless are
104+
// listed here, by project path. Once the number of formatted projects
105+
// is greater than the number of unformatted projects, this can be
106+
// switched to an exclude list, and eventualy removed completely.
107+
def projectPathsToFormat = [
108+
// ':build-tools'
109+
]
110+
111+
if (projectPathsToFormat.contains(project.path)) {
112+
project.apply plugin: "com.diffplug.gradle.spotless"
113+
114+
spotless {
115+
java {
116+
117+
removeUnusedImports()
118+
eclipse().configFile rootProject.file('.eclipseformat.xml')
119+
trimTrailingWhitespace()
120+
121+
// See CONTRIBUTING.md for details of when to enabled this.
122+
if (System.getProperty('spotless.paddedcell') != null) {
123+
paddedCell()
124+
}
125+
}
126+
}
127+
128+
precommit.dependsOn 'spotlessJavaCheck'
129+
}
101130
}
102131
}
103132

buildSrc/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ dependencies {
112112
compile 'com.netflix.nebula:gradle-extra-configurations-plugin:3.0.3'
113113
compile 'com.netflix.nebula:nebula-publishing-plugin:4.4.4'
114114
compile 'com.netflix.nebula:gradle-info-plugin:3.0.3'
115-
compile 'org.eclipse.jgit:org.eclipse.jgit:3.2.0.201312181205-r'
115+
compile 'org.eclipse.jgit:org.eclipse.jgit:5.5.0.201909110433-r'
116116
compile 'com.perforce:p4java:2012.3.551082' // THIS IS SUPPOSED TO BE OPTIONAL IN THE FUTURE....
117117
compile 'org.apache.rat:apache-rat:0.11'
118118
compile "org.elasticsearch:jna:4.5.1"

modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,8 @@ Constructor<?> compile(Loader loader, Set<String> extractedVariables, String nam
227227
}
228228

229229
return clazz.getConstructors()[0];
230-
} catch (Exception exception) { // Catch everything to let the user know this is something caused internally.
230+
} catch (Exception exception) {
231+
// Catch everything to let the user know this is something caused internally.
231232
throw new IllegalStateException("An internal error occurred attempting to define the script [" + name + "].", exception);
232233
}
233234
}

modules/lang-painless/src/main/java/org/elasticsearch/painless/LambdaBootstrap.java

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,32 +60,35 @@
6060
* {@link java.lang.invoke.LambdaMetafactory} since the Painless casting model
6161
* cannot be fully supported through this class.
6262
*
63-
* For each lambda function/method reference used within a Painless script
63+
* <p>For each lambda function/method reference used within a Painless script
6464
* a class will be generated at link-time using the
6565
* {@link LambdaBootstrap#lambdaBootstrap} method that contains the following:
66-
* 1. member fields for any captured variables
67-
* 2. a constructor that will take in captured variables and assign them to
66+
*
67+
* <ol>
68+
* <li>member fields for any captured variables
69+
* <li>a constructor that will take in captured variables and assign them to
6870
* their respective member fields
69-
* 3. a static ctor delegation method, if the lambda function is a ctor.
70-
* 4. a method that will load the member fields representing captured variables
71+
* <li>a static ctor delegation method, if the lambda function is a ctor.
72+
* <li>a method that will load the member fields representing captured variables
7173
* and take in any other necessary values based on the arguments passed into the
7274
* lambda function/reference method; it will then make a delegated call to the
73-
* actual lambda function/reference method
75+
* actual lambda function/reference method.
76+
* </ol>
7477
*
75-
* Take for example the following Painless script:
78+
* <p>Take for example the following Painless script:
7679
*
77-
* {@code
80+
* <pre>{@code
7881
* List list1 = new ArrayList(); "
7982
* list1.add(2); "
8083
* List list2 = new ArrayList(); "
8184
* list1.forEach(x -> list2.add(x));"
8285
* return list[0]"
83-
* }
86+
* }</pre>
8487
*
85-
* The script contains a lambda function with a captured variable.
88+
* <p>The script contains a lambda function with a captured variable.
8689
* The following Lambda class would be generated:
8790
*
88-
* {@code
91+
* <pre>{@code
8992
* public static final class $$Lambda0 implements Consumer {
9093
* private List arg$0;
9194
*
@@ -109,9 +112,9 @@
109112
* }
110113
* ...
111114
* }
112-
* }
115+
* }</pre>
113116
*
114-
* Also the accept method actually uses an invokedynamic
117+
* <p>Also the accept method actually uses an invokedynamic
115118
* instruction to call the lambda$0 method so that
116119
* {@link MethodHandle#asType} can be used to do the necessary
117120
* conversions between argument types without having to hard
@@ -120,7 +123,7 @@
120123
* calls the constructor. This method is used by the
121124
* invokedynamic call to initialize the instance.
122125
*
123-
* When the {@link CallSite} is linked the linked method depends
126+
* <p>When the {@link CallSite} is linked the linked method depends
124127
* on whether or not there are captures. If there are no captures
125128
* the same instance of the generated lambda class will be
126129
* returned each time by the factory method as there are no

modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngine.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,8 @@ private <T> T generateFactory(Loader loader, ScriptContext<T> context, Set<Strin
316316

317317
try {
318318
return context.factoryClazz.cast(factory.getConstructor().newInstance());
319-
} catch (Exception exception) { // Catch everything to let the user know this is something caused internally.
319+
} catch (Exception exception) {
320+
// Catch everything to let the user know this is something caused internally.
320321
throw new IllegalStateException(
321322
"An internal error occurred attempting to define the factory class [" + className + "].", exception);
322323
}

modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EAssignment.java

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,8 @@ void write(MethodWriter writer, Globals globals) {
291291
// from dup the value onto the stack
292292
}
293293

294-
lhs.store(writer, globals); // store the lhs's value from the stack in its respective variable/field/array
294+
// store the lhs's value from the stack in its respective variable/field/array
295+
lhs.store(writer, globals);
295296
} else if (operation != null) {
296297
// Handle the case where we are doing a compound assignment that
297298
// does not represent a String concatenation.
@@ -309,9 +310,9 @@ void write(MethodWriter writer, Globals globals) {
309310
// to the promotion type between the lhs and rhs types
310311
rhs.write(writer, globals); // write the bytecode for the rhs
311312

312-
// XXX: fix these types, but first we need def compound assignment tests.
313-
// its tricky here as there are possibly explicit casts, too.
314-
// write the operation instruction for compound assignment
313+
// XXX: fix these types, but first we need def compound assignment tests.
314+
// its tricky here as there are possibly explicit casts, too.
315+
// write the operation instruction for compound assignment
315316
if (promote == def.class) {
316317
writer.writeDynamicBinaryInstruction(
317318
location, promote, def.class, def.class, operation, DefBootstrap.OPERATOR_COMPOUND_ASSIGNMENT);
@@ -322,23 +323,24 @@ void write(MethodWriter writer, Globals globals) {
322323
writer.writeCast(back); // if necessary cast the promotion type value back to the lhs's type
323324

324325
if (lhs.read && !post) {
325-
writer.writeDup(MethodWriter.getType(lhs.actual).getSize(), lhs.accessElementCount()); // dup the value if the lhs is also
326-
// read from and is not a post
327-
// increment
326+
// dup the value if the lhs is also read from and is not a post increment
327+
writer.writeDup(MethodWriter.getType(lhs.actual).getSize(), lhs.accessElementCount());
328328
}
329329

330-
lhs.store(writer, globals); // store the lhs's value from the stack in its respective variable/field/array
330+
// store the lhs's value from the stack in its respective variable/field/array
331+
lhs.store(writer, globals);
331332
} else {
332333
// Handle the case for a simple write.
333334

334335
rhs.write(writer, globals); // write the bytecode for the rhs rhs
335336

336337
if (lhs.read) {
337-
writer.writeDup(MethodWriter.getType(lhs.actual).getSize(), lhs.accessElementCount()); // dup the value if the lhs
338-
// is also read from
338+
// dup the value if the lhs is also read from
339+
writer.writeDup(MethodWriter.getType(lhs.actual).getSize(), lhs.accessElementCount());
339340
}
340341

341-
lhs.store(writer, globals); // store the lhs's value from the stack in its respective variable/field/array
342+
// store the lhs's value from the stack in its respective variable/field/array
343+
lhs.store(writer, globals);
342344
}
343345
}
344346

modules/lang-painless/src/main/java/org/elasticsearch/painless/node/package-info.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@
109109
* <p>
110110
* Generally, expression nodes have member data that evaluate static and def types. The typical order for an expression node
111111
* during the analysis phase looks like the following:
112-
* {@code
112+
* <pre>{@code
113113
* For known expected types:
114114
*
115115
* expression.child.expected = expectedType // set the known expected type
@@ -132,7 +132,7 @@
132132
* expression.child = expression.child.cast(...) // add an implicit cast node if the child node's
133133
* // actual type is not the expected type and set the
134134
* // expression's child to the implicit cast node
135-
* }
135+
* }</pre>
136136
* Expression nodes just call each child during the writing phase.
137137
* <p>
138138
* Postfix nodes represent postfixes in a variable/method chain including braces, calls, or fields.

modules/lang-painless/src/test/java/org/elasticsearch/painless/ArrayLikeObjectTestCase.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ private void expectOutOfBounds(int index, String script, Object val) {
8787
*/
8888
assertThat(e.getMessage(), outOfBoundsExceptionMessageMatcher(index, 5));
8989
} catch (AssertionError ae) {
90-
ae.addSuppressed(e); // Mark the exception we are testing as suppressed so we get its stack trace.
90+
// Mark the exception we are testing as suppressed so we get its stack trace.
91+
ae.addSuppressed(e);
9192
throw ae;
9293
}
9394
}

0 commit comments

Comments
 (0)