• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

devonfw / IDEasy / 9907372175

12 Jul 2024 11:49AM UTC coverage: 61.142% (-0.02%) from 61.162%
9907372175

push

github

hohwille
fixed tests

1997 of 3595 branches covered (55.55%)

Branch coverage included in aggregate %.

5296 of 8333 relevant lines covered (63.55%)

2.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.35
cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java
1
package com.devonfw.tools.ide.common;
2

3
import java.io.File;
4
import java.io.IOException;
5
import java.nio.file.Files;
6
import java.nio.file.Path;
7
import java.util.ArrayList;
8
import java.util.HashMap;
9
import java.util.Iterator;
10
import java.util.List;
11
import java.util.Map;
12
import java.util.regex.Pattern;
13
import java.util.stream.Stream;
14

15
import com.devonfw.tools.ide.context.IdeContext;
16
import com.devonfw.tools.ide.os.SystemInfoImpl;
17
import com.devonfw.tools.ide.os.WindowsPathSyntax;
18
import com.devonfw.tools.ide.variable.IdeVariables;
19

20
/**
21
 * Represents the PATH variable in a structured way. The PATH contains the system path entries together with the entries for the IDEasy tools. The generic
22
 * system path entries are stored in a {@link List} ({@code paths}) and the tool entries are stored in a {@link Map} ({@code tool2pathMap}) as they can change
23
 * dynamically at runtime (e.g. if a new tool is installed). As the tools must have priority the actual PATH is build by first the entries for the tools and
24
 * then the generic entries from the system PATH. Such tool entries are ignored from the actual PATH of the {@link System#getenv(String) environment} at
25
 * construction time and are recomputed from the "software" folder. This is important as the initial {@link System#getenv(String) environment} PATH entries can
26
 * come from a different IDEasy project and the use may have changed projects before calling us again. Recomputing the PATH ensures side-effects from other
27
 * projects. However, it also will ensure all the entries to IDEasy locations are automatically managed and therefore cannot be managed manually be the
28
 * end-user.
29
 */
30
public class SystemPath {
31

32
  private static final Pattern REGEX_WINDOWS_PATH = Pattern.compile("([a-zA-Z]:)?(\\\\[a-zA-Z0-9\\s_.-]+)+\\\\?");
3✔
33

34
  private final String envPath;
35

36
  private final char pathSeparator;
37

38
  private final Map<String, Path> tool2pathMap;
39

40
  private final List<Path> paths;
41

42
  private final IdeContext context;
43

44
  private static final List<String> EXTENSION_PRIORITY = List.of(".exe", ".cmd", ".bat", ".msi", ".ps1", "");
9✔
45

46
  /**
47
   * The constructor.
48
   *
49
   * @param context {@link IdeContext}.
50
   */
51
  public SystemPath(IdeContext context) {
52

53
    this(context, System.getenv(IdeVariables.PATH.getName()));
6✔
54
  }
1✔
55

56
  /**
57
   * The constructor.
58
   *
59
   * @param context {@link IdeContext}.
60
   * @param envPath the value of the PATH variable.
61
   */
62
  public SystemPath(IdeContext context, String envPath) {
63

64
    this(context, envPath, context.getIdeRoot(), context.getSoftwarePath());
8✔
65
  }
1✔
66

67
  /**
68
   * The constructor.
69
   *
70
   * @param context {@link IdeContext} for the output of information.
71
   * @param envPath the value of the PATH variable.
72
   * @param ideRoot the {@link IdeContext#getIdeRoot() IDE_ROOT}.
73
   * @param softwarePath the {@link IdeContext#getSoftwarePath() software path}.
74
   */
75
  public SystemPath(IdeContext context, String envPath, Path ideRoot, Path softwarePath) {
76

77
    this(context, envPath, ideRoot, softwarePath, File.pathSeparatorChar);
7✔
78
  }
1✔
79

80
  /**
81
   * The constructor.
82
   *
83
   * @param context {@link IdeContext} for the output of information.
84
   * @param envPath the value of the PATH variable.
85
   * @param ideRoot the {@link IdeContext#getIdeRoot() IDE_ROOT}.
86
   * @param softwarePath the {@link IdeContext#getSoftwarePath() software path}.
87
   * @param pathSeparator the path separator char (';' for Windows and ':' otherwise).
88
   */
89
  public SystemPath(IdeContext context, String envPath, Path ideRoot, Path softwarePath, char pathSeparator) {
90

91
    super();
2✔
92
    this.context = context;
3✔
93
    this.envPath = envPath;
3✔
94
    this.pathSeparator = pathSeparator;
3✔
95
    this.tool2pathMap = new HashMap<>();
5✔
96
    this.paths = new ArrayList<>();
5✔
97
    String[] envPaths = envPath.split(Character.toString(pathSeparator));
5✔
98
    for (String segment : envPaths) {
16✔
99
      Path path = Path.of(segment);
5✔
100
      String tool = getTool(path, ideRoot);
4✔
101
      if (tool == null) {
2!
102
        this.paths.add(path);
5✔
103
      }
104
    }
105
    collectToolPath(softwarePath);
3✔
106
  }
1✔
107

108
  private void collectToolPath(Path softwarePath) {
109

110
    if (softwarePath == null) {
2✔
111
      return;
1✔
112
    }
113
    if (Files.isDirectory(softwarePath)) {
5✔
114
      try (Stream<Path> children = Files.list(softwarePath)) {
3✔
115
        Iterator<Path> iterator = children.iterator();
3✔
116
        while (iterator.hasNext()) {
3✔
117
          Path child = iterator.next();
4✔
118
          String tool = child.getFileName().toString();
4✔
119
          if (!"extra".equals(tool) && Files.isDirectory(child)) {
9!
120
            Path toolPath = child;
2✔
121
            Path bin = child.resolve("bin");
4✔
122
            if (Files.isDirectory(bin)) {
5✔
123
              toolPath = bin;
2✔
124
            }
125
            this.tool2pathMap.put(tool, toolPath);
6✔
126
          }
127
        }
1✔
128
      } catch (IOException e) {
×
129
        throw new IllegalStateException("Failed to list children of " + softwarePath, e);
×
130
      }
1✔
131
    }
132
  }
1✔
133

134
  private static String getTool(Path path, Path ideRoot) {
135

136
    if (ideRoot == null) {
2✔
137
      return null;
2✔
138
    }
139
    if (path.startsWith(ideRoot)) {
4!
140
      int i = ideRoot.getNameCount();
×
141
      if (path.getNameCount() > i) {
×
142
        return path.getName(i).toString();
×
143
      }
144
    }
145
    return null;
2✔
146
  }
147

148
  private Path findBinaryInOrder(Path path, String tool) {
149

150
    List<String> extensionPriority = List.of("");
3✔
151
    if (SystemInfoImpl.INSTANCE.isWindows()) {
3!
152
      extensionPriority = EXTENSION_PRIORITY;
×
153
    }
154
    for (String extension : extensionPriority) {
10✔
155

156
      Path fileToExecute = path.resolve(tool + extension);
6✔
157

158
      if (Files.exists(fileToExecute)) {
5✔
159
        return fileToExecute;
2✔
160
      }
161
    }
1✔
162

163
    return null;
2✔
164
  }
165

166
  /**
167
   * @param toolPath the {@link Path} to the tool installation.
168
   * @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.
169
   */
170
  public Path findBinary(Path toolPath) {
171

172
    Path parent = toolPath.getParent();
3✔
173
    String fileName = toolPath.getFileName().toString();
4✔
174

175
    if (parent == null) {
2!
176

177
      for (Path path : this.tool2pathMap.values()) {
12✔
178
        Path binaryPath = findBinaryInOrder(path, fileName);
5✔
179
        if (binaryPath != null) {
2✔
180
          return binaryPath;
2✔
181
        }
182
      }
1✔
183

184
      for (Path path : this.paths) {
11✔
185
        Path binaryPath = findBinaryInOrder(path, fileName);
5✔
186
        if (binaryPath != null) {
2✔
187
          return binaryPath;
2✔
188
        }
189
      }
2✔
190
    } else {
191
      Path binaryPath = findBinaryInOrder(parent, fileName);
×
192
      if (binaryPath != null) {
×
193
        return binaryPath;
×
194
      }
195
    }
196

197
    return toolPath;
2✔
198
  }
199

200
  /**
201
   * @param tool the name of the tool.
202
   * @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.
203
   */
204
  public Path getPath(String tool) {
205

206
    return this.tool2pathMap.get(tool);
6✔
207
  }
208

209
  /**
210
   * @param tool the name of the tool.
211
   * @param path the new {@link #getPath(String) tool bin path}.
212
   */
213
  public void setPath(String tool, Path path) {
214

215
    this.tool2pathMap.put(tool, path);
6✔
216
  }
1✔
217

218
  @Override
219
  public String toString() {
220

221
    return toString(null);
4✔
222
  }
223

224
  /**
225
   * @param pathSyntax the {@link WindowsPathSyntax} to convert to.
226
   * @return this {@link SystemPath} as {@link String} for the PATH environment variable.
227
   */
228
  public String toString(WindowsPathSyntax pathSyntax) {
229

230
    char separator;
231
    if (pathSyntax == WindowsPathSyntax.MSYS) {
3!
232
      separator = ':';
×
233
    } else {
234
      separator = this.pathSeparator;
3✔
235
    }
236
    StringBuilder sb = new StringBuilder(this.envPath.length() + 128);
9✔
237
    for (Path path : this.tool2pathMap.values()) {
12✔
238
      appendPath(path, sb, separator, pathSyntax);
5✔
239
    }
1✔
240
    for (Path path : this.paths) {
11✔
241
      appendPath(path, sb, separator, pathSyntax);
5✔
242
    }
1✔
243
    return sb.toString();
3✔
244
  }
245

246
  private static void appendPath(Path path, StringBuilder sb, char separator, WindowsPathSyntax pathSyntax) {
247

248
    if (sb.length() > 0) {
3✔
249
      sb.append(separator);
4✔
250
    }
251
    String pathString;
252
    if (pathSyntax == null) {
2!
253
      pathString = path.toString();
4✔
254
    } else {
255
      pathString = pathSyntax.format(path);
×
256
    }
257
    sb.append(pathString);
4✔
258
  }
1✔
259

260
  /**
261
   * Method to validate if a given path string is a Windows path or not
262
   *
263
   * @param pathString The string to check if it is a Windows path string.
264
   * @return {@code true} if it is a valid windows path string, else {@code false}.
265
   */
266
  public static boolean isValidWindowsPath(String pathString) {
267

268
    return REGEX_WINDOWS_PATH.matcher(pathString).matches();
5✔
269
  }
270
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc