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

devonfw / IDEasy / 28678637275

03 Jul 2026 06:56PM UTC coverage: 71.398% (+0.03%) from 71.368%
28678637275

push

github

web-flow
#1937: link content from settings into workspace(s) (#1983)

Co-authored-by: Jörg Hohwiller <hohwille@users.noreply.github.com>

4719 of 7304 branches covered (64.61%)

Branch coverage included in aggregate %.

12123 of 16285 relevant lines covered (74.44%)

3.15 hits per line

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

89.7
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

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

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

43
  private final Path file;
44

45
  private final Properties properties;
46

47
  private boolean invalid;
48

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

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

69
  /**
70
   * @return the ID derived from the filename without {@link IdeContext#EXT_PROPERTIES}.
71
   */
72
  public String getId() {
73

74
    String filename = this.file.getFileName().toString();
5✔
75
    if (filename.endsWith(IdeContext.EXT_PROPERTIES)) {
4✔
76
      return filename.substring(0, filename.length() - IdeContext.EXT_PROPERTIES.length());
9✔
77
    }
78
    return filename;
2✔
79
  }
80

81
  /**
82
   * @param name the name of the requested property.
83
   * @return the value of the requested property or {@code null} if undefined.
84
   */
85
  public String getProperty(String name) {
86
    return getProperty(name, false);
5✔
87
  }
88

89
  /**
90
   * @param name the name of the requested property.
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, boolean required) {
95

96
    return getProperty(name, null, required);
6✔
97
  }
98

99
  /**
100
   * @param name the name of the requested property.
101
   * @param legacyName the optional legacy property name.
102
   * @param required - {@code true} if the requested property is required, {@code false} otherwise.
103
   * @return the value of the requested property or {@code null} if undefined.
104
   */
105
  public String getProperty(String name, String legacyName, boolean required) {
106

107
    String value = doGetProperty(name, legacyName);
5✔
108
    if (isEmpty(value)) {
3✔
109
      if (required) {
2✔
110
        LOG.error("The properties file {} is invalid because the required property {} is not present. Ignoring this file.", this.file, name);
6✔
111
        this.invalid = true;
3✔
112
      }
113
      return null;
2✔
114
    }
115
    return value;
2✔
116
  }
117

118
  private String doGetProperty(String name, String legacyName) {
119

120
    String value = this.properties.getProperty(name);
5✔
121
    if (value != null) {
2✔
122
      return value;
2✔
123
    }
124
    if (legacyName != null) {
2✔
125
      value = getLegacyProperty(legacyName, name);
5✔
126
      if (value != null) {
2✔
127
        return value;
2✔
128
      }
129
    }
130
    legacyName = name.replace("_", ".");
5✔
131
    if (!legacyName.equals(name)) {
4✔
132
      value = getLegacyProperty(legacyName, name);
5✔
133
      if (value != null) {
2!
134
        return value;
×
135
      }
136
      legacyName = name.replace("_", "-");
5✔
137
      value = getLegacyProperty(legacyName, name);
5✔
138
    }
139
    return value;
2✔
140
  }
141

142
  private static boolean isEmpty(String value) {
143

144
    return (value == null) || value.isBlank();
9✔
145
  }
146

147
  private String getLegacyProperty(String legacyName, String name) {
148

149
    String value = this.properties.getProperty(legacyName);
5✔
150
    if (value != null) {
2✔
151
      LOG.warn("In the properties file {} please replace the legacy property {} with the official property {}", this.file, legacyName, name);
18✔
152
    }
153
    return value;
2✔
154
  }
155

156
  /**
157
   * @return {@code true} if these properties have been marked as invalid because a required property was requested that is not available, {@code false}
158
   *     otherwise.
159
   */
160
  public boolean isInvalid() {
161

162
    return this.invalid;
3✔
163
  }
164

165
  /**
166
   * @return the {@link RepositoryConfig#path() path}.
167
   */
168
  public String getPath() {
169

170
    return sanatizeRelativePath(getProperty(PROPERTY_PATH), PROPERTY_PATH);
7✔
171
  }
172

173
  /**
174
   * @return the {@link RepositoryConfig#workingSets() working sets}.
175
   */
176
  public String getWorkingSets() {
177

178
    return getProperty(PROPERTY_WORKING_SETS);
4✔
179
  }
180

181
  /**
182
   * @return the {@link RepositoryConfig#gitUrl()}  git url}.
183
   */
184
  public String getGitUrl() {
185

186
    return getProperty(PROPERTY_GIT_URL, !isSettingsProperties());
10✔
187
  }
188

189
  private boolean isSettingsProperties() {
190

191
    return IdeContext.FOLDER_SETTINGS.equals(getId());
5✔
192
  }
193

194
  /**
195
   * @return the {@link RepositoryConfig#gitBranch()}  git branch}.
196
   */
197
  public String getGitBranch() {
198

199
    return getProperty(PROPERTY_GIT_BRANCH);
4✔
200
  }
201

202
  /**
203
   * @return the {@link RepositoryConfig#buildPath() build path}.
204
   */
205
  public String getBuildPath() {
206

207
    return getProperty(PROPERTY_BUILD_PATH);
4✔
208
  }
209

210
  /**
211
   * @return the {@link RepositoryConfig#buildCmd() build command}.
212
   */
213
  public String getBuildCmd() {
214

215
    return getProperty(PROPERTY_BUILD_CMD);
4✔
216
  }
217

218
  /**
219
   * @return the {@link RepositoryConfig#active() active flag}.
220
   */
221
  public boolean isActive() {
222

223
    return parseBoolean(getProperty(PROPERTY_ACTIVE));
5✔
224
  }
225

226
  /**
227
   * @return the IDEs where to import the repository.
228
   */
229
  public Set<String> getImports() {
230

231
    String importProperty = getProperty(PROPERTY_IMPORT);
4✔
232
    if (importProperty != null) {
2✔
233
      if (importProperty.isEmpty()) {
3!
234
        return Set.of();
×
235
      }
236
      return Arrays.stream(importProperty.split(",")).map(String::trim).collect(Collectors.toUnmodifiableSet());
10✔
237
    }
238

239
    String legacyImportProperty = getLegacyProperty(PROPERTY_ECLIPSE, PROPERTY_IMPORT);
5✔
240
    if ("import".equals(legacyImportProperty)) {
4!
241
      LOG.warn("Property {} is deprecated and should be replaced with {} (invert key and value).", PROPERTY_ECLIPSE,
×
242
          PROPERTY_IMPORT);
243
      return Set.of("eclipse");
×
244
    } else {
245
      return Set.of();
2✔
246
    }
247
  }
248

249
  /**
250
   * @return the workspaces where to clone the repository. Returns a set containing "main" as default if not specified.
251
   */
252
  public List<String> getWorkspaces() {
253

254
    String workspaceProperty = getProperty(PROPERTY_WORKSPACES, "workspace", false);
6✔
255
    if (!isEmpty(workspaceProperty)) {
3✔
256
      if (RepositoryConfig.WORKSPACE_NAME_ALL.equals(workspaceProperty)) {
4✔
257
        return List.of(workspaceProperty);
3✔
258
      }
259
      List<String> list = new ArrayList<>();
4✔
260
      Set<String> set = new HashSet<>();
4✔
261
      for (String workspace : workspaceProperty.split(",")) {
18✔
262
        workspace = workspace.trim();
3✔
263
        if (WORKSPACE_PATTERN.matcher(workspace).matches()) {
5!
264
          boolean added = set.add(workspace);
4✔
265
          if (added) {
2!
266
            list.add(workspace);
5✔
267
          } else {
268
            LOG.warn("Ignoring duplicate workspace {} from {}", workspace, workspaceProperty);
×
269
          }
270
        } else {
1✔
271
          LOG.warn("Ignoring illegal workspace {} from {}", workspace, workspaceProperty);
×
272
        }
273
      }
274
      return Collections.unmodifiableList(list);
3✔
275
    }
276
    return List.of(IdeContext.WORKSPACE_MAIN);
3✔
277
  }
278

279
  public List<RepositoryLink> getLinks() {
280

281
    String link = getProperty(PROPERTY_LINK);
4✔
282
    if (isEmpty(link)) {
3✔
283
      return List.of();
2✔
284
    }
285
    List<RepositoryLink> links = new ArrayList<>();
4✔
286
    for (String linkItem : link.split(",")) {
18✔
287
      String linkPath = linkItem;
2✔
288
      String linkTarget = "";
2✔
289
      int eqIndex = linkItem.indexOf('=');
4✔
290
      if (eqIndex > 0) {
2✔
291
        linkPath = linkItem.substring(0, eqIndex);
5✔
292
        linkTarget = linkItem.substring(eqIndex + 1);
6✔
293
      }
294
      linkPath = sanatizeRelativePath(linkPath, PROPERTY_LINK);
5✔
295
      if (!linkTarget.isEmpty()) {
3✔
296
        linkTarget = sanatizeRelativePath(linkTarget, PROPERTY_LINK_TARGET);
5✔
297
      }
298
      if ((linkPath != null) && (linkTarget != null)) {
4!
299
        links.add(new RepositoryLink(linkPath, linkTarget));
8✔
300
      }
301
    }
302
    return List.copyOf(links); // make immutable for record
3✔
303
  }
304

305
  private String sanatizeRelativePath(String path, String propertyName) {
306
    if (path == null) {
2✔
307
      return null;
2✔
308
    }
309
    String normalized = path.trim().replace('\\', '/');
6✔
310
    if (normalized.contains("..") || !PATH_PATTERN.matcher(normalized).matches()) {
9!
311
      LOG.warn("Invalid path {} from property {} of {}", path, propertyName, this.file);
×
312
      return null;
×
313
    }
314
    return normalized;
2✔
315
  }
316

317
  private static boolean parseBoolean(String value) {
318

319
    if (value == null) {
2✔
320
      return true;
2✔
321
    }
322
    return "true".equals(value.trim());
5✔
323
  }
324
}
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