-
Notifications
You must be signed in to change notification settings - Fork 36
/
Copy pathSystemPath.java
233 lines (194 loc) · 6.42 KB
/
SystemPath.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
package com.devonfw.tools.ide.common;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import com.devonfw.tools.ide.context.IdeContext;
/**
* Represents the PATH variable in a structured way.
*/
public class SystemPath {
private final String envPath;
private final char pathSeparator;
private final Map<String, Path> tool2pathMap;
private final List<Path> paths;
private final IdeContext context;
private static final List<String> EXTENSION_PRIORITY = List.of(".exe", ".cmd", ".bat", ".msi", ".ps1", "");
/**
* The constructor.
*
* @param envPath the value of the PATH variable.
* @param softwarePath the {@link IdeContext#getSoftwarePath() software path}.
* @param context {@link IdeContext} for the output of information.
*/
public SystemPath(String envPath, Path softwarePath, IdeContext context) {
this(envPath, softwarePath, File.pathSeparatorChar, context);
}
/**
* The constructor.
*
* @param envPath the value of the PATH variable.
* @param softwarePath the {@link IdeContext#getSoftwarePath() software path}.
* @param pathSeparator the path separator char (';' for Windows and ':' otherwise).
* @param context {@link IdeContext} for the output of information.
*/
public SystemPath(String envPath, Path softwarePath, char pathSeparator, IdeContext context) {
super();
this.context = context;
this.envPath = envPath;
this.pathSeparator = pathSeparator;
this.tool2pathMap = new HashMap<>();
this.paths = new ArrayList<>();
String[] envPaths = envPath.split(Character.toString(pathSeparator));
for (String segment : envPaths) {
Path path = Path.of(segment);
String tool = getTool(path, softwarePath);
if (tool == null) {
this.paths.add(path);
} else {
Path duplicate = this.tool2pathMap.putIfAbsent(tool, path);
if (duplicate != null) {
context.warning("Duplicated tool path for tool: {} at path: {} with duplicated path: {}.", tool, path,
duplicate);
}
}
}
collectToolPath(softwarePath);
}
private void collectToolPath(Path softwarePath) {
if (softwarePath == null) {
return;
}
if (Files.isDirectory(softwarePath)) {
try (Stream<Path> children = Files.list(softwarePath)) {
Iterator<Path> iterator = children.iterator();
while (iterator.hasNext()) {
Path child = iterator.next();
if (Files.isDirectory(child)) {
Path toolPath = child;
Path bin = child.resolve("bin");
if (Files.isDirectory(bin)) {
toolPath = bin;
}
this.paths.add(0, toolPath);
this.tool2pathMap.put(child.getFileName().toString(), toolPath);
}
}
} catch (IOException e) {
throw new IllegalStateException("Failed to list children of " + softwarePath, e);
}
}
}
private static String getTool(Path path, Path softwarePath) {
if (softwarePath == null) {
return null;
}
if (path.startsWith(softwarePath)) {
int i = softwarePath.getNameCount();
if (path.getNameCount() > i) {
return path.getName(i).toString();
}
}
return null;
}
private Path findBinaryInOrder(Path path, String tool) {
for (String extension : EXTENSION_PRIORITY) {
Path fileToExecute = path.resolve(tool + extension);
if (Files.exists(fileToExecute)) {
return fileToExecute;
}
}
return null;
}
/**
* @param toolPath the {@link Path} to the tool installation.
* @return the {@link Path} to the binary executable of the tool. E.g. is "software/mvn" is given
* "software/mvn/bin/mvn" could be returned.
*/
public Path findBinary(Path toolPath) {
Path parent = toolPath.getParent();
String fileName = toolPath.getFileName().toString();
if (parent == null) {
for (Path path : tool2pathMap.values()) {
Path binaryPath = findBinaryInOrder(path, fileName);
if (binaryPath != null) {
return binaryPath;
}
}
for (Path path : this.paths) {
Path binaryPath = findBinaryInOrder(path, fileName);
if (binaryPath != null) {
return binaryPath;
}
}
} else {
Path binaryPath = findBinaryInOrder(parent, fileName);
if (binaryPath != null) {
return binaryPath;
}
}
return toolPath;
}
/**
* @param tool the name of the tool.
* @return the {@link Path} to the directory of the tool where the binaries can be found or {@code null} if the tool
* is not installed.
*/
public Path getPath(String tool) {
return this.tool2pathMap.get(tool);
}
/**
* @param tool the name of the tool.
* @param path the new {@link #getPath(String) tool bin path}.
*/
public void setPath(String tool, Path path) {
this.tool2pathMap.put(tool, path);
}
@Override
public String toString() {
return toString(false);
}
/**
* @param bash - {@code true} to convert the PATH to bash syntax (relevant for git-bash or cygwin on windows),
* {@code false} otherwise.
* @return this {@link SystemPath} as {@link String} for the PATH environment variable.
*/
public String toString(boolean bash) {
char separator;
if (bash) {
separator = ':';
} else {
separator = this.pathSeparator;
}
StringBuilder sb = new StringBuilder(this.envPath.length() + 128);
for (Path path : this.tool2pathMap.values()) {
appendPath(path, sb, separator, bash);
}
for (Path path : this.paths) {
appendPath(path, sb, separator, bash);
}
return sb.toString();
}
private void appendPath(Path path, StringBuilder sb, char separator, boolean bash) {
if (sb.length() > 0) {
sb.append(separator);
}
String pathString = path.toString();
if (bash && (pathString.length() > 3) && (pathString.charAt(1) == ':')) {
char slash = pathString.charAt(2);
if ((slash == '\\') || (slash == '/')) {
char drive = Character.toLowerCase(pathString.charAt(0));
if ((drive >= 'a') && (drive <= 'z')) {
pathString = "/" + drive + pathString.substring(2).replace('\\', '/');
}
}
}
sb.append(pathString);
}
}