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

devonfw / IDEasy / 27688586400

17 Jun 2026 12:22PM UTC coverage: 71.292% (+0.007%) from 71.285%
27688586400

Pull #1983

github

web-flow
Merge 096f05f93 into ec9c73a45
Pull Request #1983: #1937: link content from settings into workspace(s)

4690 of 7268 branches covered (64.53%)

Branch coverage included in aggregate %.

12063 of 16231 relevant lines covered (74.32%)

3.15 hits per line

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

89.31
cli/src/main/java/com/devonfw/tools/ide/git/repository/RepositoryProperties.java
1
package com.devonfw.tools.ide.git.repository;
2

3
import java.nio.file.Path;
4
import java.util.ArrayList;
5
import java.util.Arrays;
6
import java.util.Collections;
7
import java.util.HashSet;
8
import java.util.List;
9
import java.util.Properties;
10
import java.util.Set;
11
import java.util.regex.Pattern;
12
import java.util.stream.Collectors;
13

14
import org.slf4j.Logger;
15
import org.slf4j.LoggerFactory;
16

17
import com.devonfw.tools.ide.context.IdeContext;
18

19
/**
20
 * {@link Properties} for {@link RepositoryConfig}.
21
 */
22
final class RepositoryProperties {
23

24
  private static final Logger LOG = LoggerFactory.getLogger(RepositoryProperties.class);
3✔
25

26
  private static final String PROPERTY_PATH = "path";
27
  private static final String PROPERTY_WORKING_SETS = "workingsets";
28
  private static final String PROPERTY_WORKSPACES = "workspaces";
29
  private static final String PROPERTY_GIT_URL = "git_url";
30
  private static final String PROPERTY_BUILD_PATH = "build_path";
31
  private static final String PROPERTY_BUILD_CMD = "build_cmd";
32
  private static final String PROPERTY_ACTIVE = "active";
33
  private static final String PROPERTY_GIT_BRANCH = "git_branch";
34
  private static final String PROPERTY_IMPORT = "import";
35
  private static final String PROPERTY_LINK = "link";
36
  private static final String PROPERTY_LINK_TARGET = "link (=<target>)";
37
  private static final String PROPERTY_ECLIPSE = "eclipse";
38
  private static final String SETTINGS_PROPERTIES = "settings.properties";
39

40
  private static final Pattern PATH_PATTERN = Pattern.compile("[a-zA-Z0-9_.$/-]+");
3✔
41

42
  private static final Pattern WORKSPACE_PATTERN = Pattern.compile("[a-zA-Z][a-zA-Z0-9_.-]+");
4✔
43

44
  private final Path file;
45

46
  private final Properties properties;
47

48
  private boolean invalid;
49

50
  /**
51
   * The constructor.
52
   *
53
   * @param file the {@link Path} to the properties file.
54
   * @param context the {@link IdeContext}.
55
   */
56
  public RepositoryProperties(Path file, IdeContext context) {
57
    this(file, context.getFileAccess().readProperties(file));
7✔
58
  }
1✔
59

60
  /**
61
   * @param file the {@link Path} to the properties file.
62
   * @param properties the actual {@link Properties} loaded from the file.
63
   */
64
  RepositoryProperties(Path file, Properties properties) {
65
    super();
2✔
66
    this.file = file;
3✔
67
    this.properties = properties;
3✔
68
  }
1✔
69

70
  /**
71
   * @param name the name of the requested property.
72
   * @return the value of the requested property or {@code null} if undefined.
73
   */
74
  public String getProperty(String name) {
75
    return getProperty(name, false);
5✔
76
  }
77

78
  /**
79
   * @param name the name of the requested property.
80
   * @param required - {@code true} if the requested property is required, {@code false} otherwise.
81
   * @return the value of the requested property or {@code null} if undefined.
82
   */
83
  public String getProperty(String name, boolean required) {
84

85
    return getProperty(name, null, required);
6✔
86
  }
87

88
  /**
89
   * @param name the name of the requested property.
90
   * @param legacyName the optional legacy property name.
91
   * @param required - {@code true} if the requested property is required, {@code false} otherwise.
92
   * @return the value of the requested property or {@code null} if undefined.
93
   */
94
  public String getProperty(String name, String legacyName, boolean required) {
95

96
    String value = doGetProperty(name, legacyName);
5✔
97
    if (isEmpty(value)) {
3✔
98
      if (required) {
2✔
99
        LOG.error("The properties file {} is invalid because the required property {} is not present. Ignoring this file.", this.file, name);
6✔
100
        this.invalid = true;
3✔
101
      }
102
      return null;
2✔
103
    }
104
    return value;
2✔
105
  }
106

107
  private String doGetProperty(String name, String legacyName) {
108

109
    String value = this.properties.getProperty(name);
5✔
110
    if (value != null) {
2✔
111
      return value;
2✔
112
    }
113
    if (legacyName != null) {
2✔
114
      value = getLegacyProperty(legacyName, name);
5✔
115
      if (value != null) {
2✔
116
        return value;
2✔
117
      }
118
    }
119
    legacyName = name.replace("_", ".");
5✔
120
    if (!legacyName.equals(name)) {
4✔
121
      value = getLegacyProperty(legacyName, name);
5✔
122
      if (value != null) {
2!
123
        return value;
×
124
      }
125
      legacyName = name.replace("_", "-");
5✔
126
      value = getLegacyProperty(legacyName, name);
5✔
127
    }
128
    return value;
2✔
129
  }
130

131
  private static boolean isEmpty(String value) {
132

133
    return (value == null) || value.isBlank();
9✔
134
  }
135

136
  private String getLegacyProperty(String legacyName, String name) {
137

138
    String value = this.properties.getProperty(legacyName);
5✔
139
    if (value != null) {
2✔
140
      LOG.warn("In the properties file {} please replace the legacy property {} with the official property {}", this.file, legacyName, name);
18✔
141
    }
142
    return value;
2✔
143
  }
144

145
  /**
146
   * @return {@code true} if these properties have been marked as invalid because a required property was requested that is not available, {@code false}
147
   *     otherwise.
148
   */
149
  public boolean isInvalid() {
150

151
    return this.invalid;
3✔
152
  }
153

154
  /**
155
   * @return the {@link RepositoryConfig#path() path}.
156
   */
157
  public String getPath() {
158

159
    return sanatizeRelativePath(getProperty(PROPERTY_PATH), PROPERTY_PATH);
7✔
160
  }
161

162
  /**
163
   * @return the {@link RepositoryConfig#workingSets() working sets}.
164
   */
165
  public String getWorkingSets() {
166

167
    return getProperty(PROPERTY_WORKING_SETS);
4✔
168
  }
169

170
  /**
171
   * @return the {@link RepositoryConfig#gitUrl()}  git url}.
172
   */
173
  public String getGitUrl() {
174

175
    return getProperty(PROPERTY_GIT_URL, !isSettingsProperties());
10✔
176
  }
177

178
  private boolean isSettingsProperties() {
179

180
    return SETTINGS_PROPERTIES.equals(this.file.getFileName().toString());
7✔
181
  }
182

183
  /**
184
   * @return the {@link RepositoryConfig#gitBranch()}  git branch}.
185
   */
186
  public String getGitBranch() {
187

188
    return getProperty(PROPERTY_GIT_BRANCH);
4✔
189
  }
190

191
  /**
192
   * @return the {@link RepositoryConfig#buildPath() build path}.
193
   */
194
  public String getBuildPath() {
195

196
    return getProperty(PROPERTY_BUILD_PATH);
4✔
197
  }
198

199
  /**
200
   * @return the {@link RepositoryConfig#buildCmd() build command}.
201
   */
202
  public String getBuildCmd() {
203

204
    return getProperty(PROPERTY_BUILD_CMD);
4✔
205
  }
206

207
  /**
208
   * @return the {@link RepositoryConfig#active() active flag}.
209
   */
210
  public boolean isActive() {
211

212
    return parseBoolean(getProperty(PROPERTY_ACTIVE));
5✔
213
  }
214

215
  /**
216
   * @return the IDEs where to import the repository.
217
   */
218
  public Set<String> getImports() {
219

220
    String importProperty = getProperty(PROPERTY_IMPORT);
4✔
221
    if (importProperty != null) {
2✔
222
      if (importProperty.isEmpty()) {
3!
223
        return Set.of();
×
224
      }
225
      return Arrays.stream(importProperty.split(",")).map(String::trim).collect(Collectors.toUnmodifiableSet());
10✔
226
    }
227

228
    String legacyImportProperty = getLegacyProperty(PROPERTY_ECLIPSE, PROPERTY_IMPORT);
5✔
229
    if ("import".equals(legacyImportProperty)) {
4!
230
      LOG.warn("Property {} is deprecated and should be replaced with {} (invert key and value).", PROPERTY_ECLIPSE,
×
231
          PROPERTY_IMPORT);
232
      return Set.of("eclipse");
×
233
    } else {
234
      return Set.of();
2✔
235
    }
236
  }
237

238
  /**
239
   * @return the workspaces where to clone the repository. Returns a set containing "main" as default if not specified.
240
   */
241
  public List<String> getWorkspaces() {
242

243
    String workspaceProperty = getProperty(PROPERTY_WORKSPACES, "workspace", false);
6✔
244
    if (!isEmpty(workspaceProperty)) {
3✔
245
      if (RepositoryConfig.WORKSPACE_NAME_ALL.equals(workspaceProperty)) {
4✔
246
        return List.of(workspaceProperty);
3✔
247
      }
248
      List<String> list = new ArrayList<>();
4✔
249
      Set<String> set = new HashSet<>();
4✔
250
      for (String workspace : workspaceProperty.split(",")) {
18✔
251
        workspace = workspace.trim();
3✔
252
        if (WORKSPACE_PATTERN.matcher(workspace).matches()) {
5!
253
          boolean added = set.add(workspace);
4✔
254
          if (added) {
2!
255
            list.add(workspace);
5✔
256
          } else {
257
            LOG.warn("Ignoring duplicate workspace {} from {}", workspace, workspaceProperty);
×
258
          }
259
        } else {
1✔
260
          LOG.warn("Ignoring illegal workspace {} from {}", workspace, workspaceProperty);
×
261
        }
262
      }
263
      return Collections.unmodifiableList(list);
3✔
264
    }
265
    return List.of(IdeContext.WORKSPACE_MAIN);
3✔
266
  }
267

268
  public List<RepositoryLink> getLinks() {
269

270
    String link = getProperty(PROPERTY_LINK);
4✔
271
    if (isEmpty(link)) {
3✔
272
      return List.of();
2✔
273
    }
274
    List<RepositoryLink> links = new ArrayList<>();
4✔
275
    for (String linkItem : link.split(",")) {
18✔
276
      String linkPath = linkItem;
2✔
277
      String linkTarget = "";
2✔
278
      int eqIndex = linkItem.indexOf('=');
4✔
279
      if (eqIndex > 0) {
2✔
280
        linkPath = linkItem.substring(0, eqIndex);
5✔
281
        linkTarget = linkItem.substring(eqIndex + 1);
6✔
282
      }
283
      linkPath = sanatizeRelativePath(linkPath, PROPERTY_LINK);
5✔
284
      if (!linkTarget.isEmpty()) {
3✔
285
        linkTarget = sanatizeRelativePath(linkTarget, PROPERTY_LINK_TARGET);
5✔
286
      }
287
      if ((linkPath != null) && (linkTarget != null)) {
4!
288
        links.add(new RepositoryLink(linkPath, linkTarget));
8✔
289
      }
290
    }
291
    return List.copyOf(links); // make immutable for record
3✔
292
  }
293

294
  private String sanatizeRelativePath(String path, String propertyName) {
295
    if (path == null) {
2✔
296
      return null;
2✔
297
    }
298
    String normalized = path.trim().replace('\\', '/');
6✔
299
    if (normalized.contains("..") || !PATH_PATTERN.matcher(normalized).matches()) {
9!
300
      LOG.warn("Invalid path {} from property {} of {}", path, propertyName, this.file);
×
301
      return null;
×
302
    }
303
    return normalized;
2✔
304
  }
305

306
  private static boolean parseBoolean(String value) {
307

308
    if (value == null) {
2✔
309
      return true;
2✔
310
    }
311
    return "true".equals(value.trim());
5✔
312
  }
313
}
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

© 2026 Coveralls, Inc