16
16
package com .diffplug .spotless .generic ;
17
17
18
18
import java .io .File ;
19
+ import java .io .FileOutputStream ;
19
20
import java .io .IOException ;
20
21
import java .io .Serializable ;
21
22
import java .nio .charset .StandardCharsets ;
22
23
import java .nio .file .Files ;
24
+ import java .nio .file .Path ;
25
+ import java .util .HashMap ;
23
26
import java .util .List ;
24
27
import java .util .Map ;
25
28
import java .util .Objects ;
29
+ import java .util .Properties ;
30
+ import java .util .TreeMap ;
31
+ import java .util .UUID ;
26
32
import java .util .regex .Pattern ;
27
33
import java .util .stream .Collectors ;
28
34
import java .util .stream .Stream ;
@@ -44,12 +50,16 @@ public final class IdeaStep {
44
50
45
51
public static final String NAME = "IDEA" ;
46
52
53
+ public static final String IDEA_EXECUTABLE_DEFAULT = "idea" ;
54
+
47
55
private static final Logger LOGGER = LoggerFactory .getLogger (IdeaStep .class );
56
+ public static final String IDEA_CONFIG_PATH_PROPERTY = "idea.config.path" ;
57
+ public static final String IDEA_SYSTEM_PATH_PROPERTY = "idea.system.path" ;
48
58
49
59
private IdeaStep () {}
50
60
51
- public static IdeaStepBuilder create () {
52
- return new IdeaStepBuilder ();
61
+ public static IdeaStepBuilder create (@ Nonnull File buildDir ) {
62
+ return new IdeaStepBuilder (Objects . requireNonNull ( buildDir ) );
53
63
}
54
64
55
65
private static FormatterStep create (@ Nonnull IdeaStepBuilder builder ) {
@@ -64,13 +74,20 @@ private static State createState(@Nonnull IdeaStepBuilder builder) {
64
74
}
65
75
66
76
public static final class IdeaStepBuilder {
67
- private static final String DEFAULT_IDEA = "idea" ;
68
77
69
78
private boolean useDefaults = true ;
70
79
@ Nonnull
71
- private String binaryPath = DEFAULT_IDEA ;
80
+ private String binaryPath = IDEA_EXECUTABLE_DEFAULT ;
72
81
@ Nullable
73
82
private String codeStyleSettingsPath ;
83
+ private final Map <String , String > ideaProperties = new HashMap <>();
84
+
85
+ @ Nonnull
86
+ private final File buildDir ;
87
+
88
+ private IdeaStepBuilder (@ Nonnull File buildDir ) {
89
+ this .buildDir = Objects .requireNonNull (buildDir );
90
+ }
74
91
75
92
public IdeaStepBuilder setUseDefaults (boolean useDefaults ) {
76
93
this .useDefaults = useDefaults ;
@@ -87,6 +104,14 @@ public IdeaStepBuilder setCodeStyleSettingsPath(@Nullable String codeStyleSettin
87
104
return this ;
88
105
}
89
106
107
+ public IdeaStepBuilder setIdeaProperties (@ Nonnull Map <String , String > ideaProperties ) {
108
+ if (ideaProperties .containsKey (IDEA_CONFIG_PATH_PROPERTY ) || ideaProperties .containsKey (IDEA_SYSTEM_PATH_PROPERTY )) {
109
+ throw new IllegalArgumentException ("Cannot override IDEA config or system path" );
110
+ }
111
+ this .ideaProperties .putAll (ideaProperties );
112
+ return this ;
113
+ }
114
+
90
115
public FormatterStep build () {
91
116
return create (this );
92
117
}
@@ -97,30 +122,35 @@ private static class State
97
122
98
123
private static final long serialVersionUID = -1825662355363926318L ;
99
124
100
- private String binaryPath ;
125
+ private final String buildDir ;
126
+ private final String uniquePath ;
127
+ private final String binaryPath ;
101
128
@ Nullable
102
- private String codeStyleSettingsPath ;
103
- private boolean withDefaults ;
129
+ private final String codeStyleSettingsPath ; // TODO make sure to save content in state
130
+ private final boolean withDefaults ;
131
+ private final TreeMap <String , String > ideaProperties ;
104
132
105
133
private State (@ Nonnull IdeaStepBuilder builder ) {
134
+ this .buildDir = ThrowingEx .get (builder .buildDir ::getCanonicalPath );
135
+ this .uniquePath = UUID .randomUUID ().toString ();
106
136
this .withDefaults = builder .useDefaults ;
107
137
this .codeStyleSettingsPath = builder .codeStyleSettingsPath ;
108
- this .binaryPath = builder .binaryPath ;
109
- resolveFullBinaryPathAndCheckVersion ();
138
+ this .ideaProperties = new TreeMap <>( builder .ideaProperties ) ;
139
+ this . binaryPath = resolveFullBinaryPathAndCheckVersion (builder . binaryPath );
110
140
}
111
141
112
- private void resolveFullBinaryPathAndCheckVersion () {
142
+ private static String resolveFullBinaryPathAndCheckVersion (String binaryPath ) {
113
143
var exe = ForeignExe
114
- .nameAndVersion (this . binaryPath , "IntelliJ IDEA" )
115
- .pathToExe (pathToExe ())
144
+ .nameAndVersion (binaryPath , "IntelliJ IDEA" )
145
+ .pathToExe (pathToExe (binaryPath ))
116
146
.versionRegex (Pattern .compile ("(IntelliJ IDEA) .*" ))
117
147
.fixCantFind (
118
148
"IDEA executable cannot be found on your machine, "
119
149
+ "please install it and put idea binary to PATH, provide a valid path to the executable or report the problem" )
120
150
.fixWrongVersion ("Provided binary is not IDEA, "
121
151
+ "please check it and fix the problem; or report the problem" );
122
152
try {
123
- this . binaryPath = exe .confirmVersionAndGetAbsolutePath ();
153
+ return exe .confirmVersionAndGetAbsolutePath ();
124
154
} catch (IOException e ) {
125
155
throw new IllegalArgumentException ("binary cannot be found" , e );
126
156
} catch (InterruptedException e ) {
@@ -130,10 +160,7 @@ private void resolveFullBinaryPathAndCheckVersion() {
130
160
}
131
161
132
162
@ CheckForNull
133
- private String pathToExe () {
134
- if (binaryPath == null ) {
135
- throw new IllegalStateException ("binaryPath is not set" );
136
- }
163
+ private static String pathToExe (String binaryPath ) {
137
164
return macOsFix (binaryPath );
138
165
}
139
166
@@ -177,7 +204,8 @@ public String applyWithFile(String unix, File file) throws Exception {
177
204
List <String > params = getParams (tempFile );
178
205
179
206
try (ProcessRunner runner = new ProcessRunner ()) {
180
- var result = runner .exec (params );
207
+ Map <String , String > env = createEnv ();
208
+ var result = runner .exec (null , env , null , params );
181
209
LOGGER .debug ("command finished with stdout: {}" ,
182
210
result .assertExitZero (StandardCharsets .UTF_8 ));
183
211
@@ -188,6 +216,38 @@ public String applyWithFile(String unix, File file) throws Exception {
188
216
}
189
217
}
190
218
219
+ private Map <String , String > createEnv () {
220
+ File ideaProps = createIdeaPropertiesFile ();
221
+ Map <String , String > env = Map .ofEntries (
222
+ Map .entry ("IDEA_PROPERTIES" , ThrowingEx .get (ideaProps ::getCanonicalPath )));
223
+ return env ;
224
+ }
225
+
226
+ private File createIdeaPropertiesFile () {
227
+ Path ideaProps = new File (buildDir ).toPath ().resolve (uniquePath ).resolve ("idea.properties" );
228
+
229
+ if (Files .exists (ideaProps )) {
230
+ return ideaProps .toFile (); // only create if it does not exist
231
+ }
232
+
233
+ ThrowingEx .run (() -> Files .createDirectories (ideaProps .getParent ()));
234
+
235
+ Path configPath = ideaProps .getParent ().resolve ("config" );
236
+ Path systemPath = ideaProps .getParent ().resolve ("system" );
237
+
238
+ Properties properties = new Properties ();
239
+ properties .putAll (ideaProperties );
240
+ properties .put (IDEA_CONFIG_PATH_PROPERTY , ThrowingEx .get (configPath .toFile ()::getCanonicalPath ));
241
+ properties .put (IDEA_SYSTEM_PATH_PROPERTY , ThrowingEx .get (systemPath .toFile ()::getCanonicalPath ));
242
+
243
+ try (FileOutputStream out = new FileOutputStream (ideaProps .toFile ())) {
244
+ properties .store (out , "Generated by spotless" );
245
+ } catch (IOException e ) {
246
+ throw new IllegalStateException ("Failed to create IDEA properties file" , e );
247
+ }
248
+ return ideaProps .toFile ();
249
+ }
250
+
191
251
private List <String > getParams (File file ) {
192
252
/* https://www.jetbrains.com/help/idea/command-line-formatter.html */
193
253
var builder = Stream .<String > builder ();
0 commit comments