1 // =================== DO NOT EDIT THIS FILE ====================
2 // Generated by Modello Velocity from model.vm
3 // template, any modifications will be overwritten.
4 // ==============================================================
5 package org.apache.maven.api.model;
6
7 import java.io.Serializable;
8 import java.util.Collections;
9 import java.util.HashMap;
10 import java.util.Map;
11 import java.util.Objects;
12 import java.util.Optional;
13 import java.util.Set;
14 import java.util.stream.Collectors;
15 import java.util.stream.Stream;
16 import org.apache.maven.api.annotations.Experimental;
17 import org.apache.maven.api.annotations.Generated;
18 import org.apache.maven.api.annotations.Immutable;
19 import org.apache.maven.api.annotations.Nonnull;
20 import org.apache.maven.api.annotations.NotThreadSafe;
21 import org.apache.maven.api.annotations.ThreadSafe;
22
23 /**
24 * The conditions within the build runtime environment which will trigger the
25 * automatic inclusion of the build profile. Multiple conditions can be defined, which must
26 * be all satisfied to activate the profile.
27 *
28 * <p>In addition to the traditional activation mechanisms (JDK version, OS properties,
29 * file existence, etc.), Maven now supports a powerful condition-based activation
30 * through the {@code condition} field. This new mechanism allows for more flexible
31 * and expressive profile activation rules.</p>
32 *
33 * <h2>Condition Syntax</h2>
34 *
35 * <p>The condition is specified as a string expression that can include various
36 * functions, comparisons, and logical operators. Some key features include:</p>
37 *
38 * <ul>
39 * <li>Property access: {@code ${property.name}}</li>
40 * <li>Comparison operators: {@code ==}, {@code !=}, {@code <}, {@code >}, {@code <=}, {@code >=}</li>
41 * <li>Logical operators: {@code &&} (AND), {@code ||} (OR), {@code not(...)}</li>
42 * <li>Functions: {@code exists(...)}, {@code missing(...)}, {@code matches(...)}, {@code inrange(...)}, and more</li>
43 * </ul>
44 *
45 * <h2>Supported Functions</h2>
46 *
47 * <p>The following functions are supported in condition expressions:</p>
48 *
49 * <ul>
50 * <li>{@code length(string)}: Returns the length of the given string.</li>
51 * <li>{@code upper(string)}: Converts the string to uppercase.</li>
52 * <li>{@code lower(string)}: Converts the string to lowercase.</li>
53 * <li>{@code substring(string, start, [end])}: Returns a substring of the given string.</li>
54 * <li>{@code indexOf(string, substring)}: Returns the index of the first occurrence of substring in string, or -1 if not found.</li>
55 * <li>{@code contains(string, substring)}: Checks if the string contains the substring.</li>
56 * <li>{@code matches(string, regex)}: Checks if the string matches the given regular expression.</li>
57 * <li>{@code not(condition)}: Negates the given condition.</li>
58 * <li>{@code if(condition, trueValue, falseValue)}: Returns trueValue if the condition is true, falseValue otherwise.</li>
59 * <li>{@code exists(path)}: Checks if a file matching the given glob pattern exists.</li>
60 * <li>{@code missing(path)}: Checks if a file matching the given glob pattern does not exist.</li>
61 * <li>{@code inrange(version, range)}: Checks if the given version is within the specified version range.</li>
62 * </ul>
63 *
64 * <h2>Supported properties</h2>
65 *
66 * <p>The following properties are supported in expressions:</p>
67 *
68 * <ul>
69 * <li>`project.basedir`: The project directory</li>
70 * <li>`project.rootDirectory`: The root directory of the project</li>
71 * <li>`project.artifactId`: The artifactId of the project</li>
72 * <li>`project.packaging`: The packaging of the project</li>
73 * <li>user properties</li>
74 * <li>system properties (including environment variables prefixed with `env.`)</li>
75 * </ul>
76 *
77 * <h2>Examples</h2>
78 *
79 * <ul>
80 * <li>JDK version range: {@code inrange(${java.version}, '[11,)')} (JDK 11 or higher)</li>
81 * <li>OS check: {@code ${os.name} == 'windows'}</li>
82 * <li>File existence: {@code exists('${project.basedir}/src/**}{@code /*.xsd')}</li>
83 * <li>Property check: {@code ${my.property} != 'some-value'}</li>
84 * <li>Regex matching: {@code matches(${os.version}, '.*aws')}</li>
85 * <li>Complex condition: {@code ${os.name} == 'windows' && ${os.arch} != 'amd64' && inrange(${os.version}, '[10,)')}</li>
86 * <li>String length check: {@code length(${user.name}) > 5}</li>
87 * <li>Substring with version: {@code substring(${java.version}, 0, 3) == '1.8'}</li>
88 * <li>Using indexOf: {@code indexOf(${java.version}, '-') > 0}</li>
89 * <li>Conditional logic: {@code if(contains(${java.version}, '-'), substring(${java.version}, 0, indexOf(${java.version}, '-')), ${java.version})}</li>
90 * </ul>
91 *
92 * <p>This flexible condition mechanism allows for more precise control over profile
93 * activation, enabling developers to create profiles that respond to a wide range of
94 * environmental factors and project states.</p>
95 */
96 @Experimental
97 @Generated @ThreadSafe @Immutable
98 public class Activation
99 implements Serializable, InputLocationTracker
100 {
101 /**
102 * If set to true, this profile will be active unless another profile in this
103 * pom is activated using the command line -P option or by one of that profile's
104 * activators.
105 */
106 final boolean activeByDefault;
107 /**
108 * Specifies that this profile will be activated when a matching JDK is detected.
109 * For example, {@code 1.4} only activates on JDKs versioned 1.4,
110 * while {@code !1.4} matches any JDK that is not version 1.4. Ranges are supported too:
111 * {@code [1.5,)} activates when the JDK is 1.5 minimum.
112 */
113 final String jdk;
114 /**
115 * Specifies that this profile will be activated when matching operating system
116 * attributes are detected.
117 */
118 final ActivationOS os;
119 /**
120 * Specifies that this profile will be activated when this property is
121 * specified.
122 */
123 final ActivationProperty property;
124 /**
125 * Specifies that this profile will be activated based on existence of a file.
126 */
127 final ActivationFile file;
128 /**
129 * Specifies that this profile will be activated based on the project's packaging.
130 */
131 final String packaging;
132 /**
133 * The condition which must be satisfied to activate the profile.
134 */
135 final String condition;
136 /** Locations */
137 final Map<Object, InputLocation> locations;
138 /** Location tracking */
139 final InputLocation importedFrom;
140
141 /**
142 * Constructor for this class, to be called from its subclasses and {@link Builder}.
143 * @see Builder#build()
144 */
145 protected Activation(Builder builder) {
146 this.activeByDefault = builder.activeByDefault != null ? builder.activeByDefault : (builder.base != null ? builder.base.activeByDefault : false);
147 this.jdk = builder.jdk != null ? builder.jdk : (builder.base != null ? builder.base.jdk : null);
148 this.os = builder.os != null ? builder.os : (builder.base != null ? builder.base.os : null);
149 this.property = builder.property != null ? builder.property : (builder.base != null ? builder.base.property : null);
150 this.file = builder.file != null ? builder.file : (builder.base != null ? builder.base.file : null);
151 this.packaging = builder.packaging != null ? builder.packaging : (builder.base != null ? builder.base.packaging : null);
152 this.condition = builder.condition != null ? builder.condition : (builder.base != null ? builder.base.condition : null);
153 this.locations = builder.computeLocations();
154 this.importedFrom = builder.importedFrom;
155 }
156
157 /**
158 * If set to true, this profile will be active unless another profile in this
159 * pom is activated using the command line -P option or by one of that profile's
160 * activators.
161 *
162 * @return a {@code boolean}
163 */
164 public boolean isActiveByDefault() {
165 return this.activeByDefault;
166 }
167
168 /**
169 * Specifies that this profile will be activated when a matching JDK is detected.
170 * For example, {@code 1.4} only activates on JDKs versioned 1.4,
171 * while {@code !1.4} matches any JDK that is not version 1.4. Ranges are supported too:
172 * {@code [1.5,)} activates when the JDK is 1.5 minimum.
173 *
174 * @return a {@code String}
175 */
176 public String getJdk() {
177 return this.jdk;
178 }
179
180 /**
181 * Specifies that this profile will be activated when matching operating system
182 * attributes are detected.
183 *
184 * @return a {@code ActivationOS}
185 */
186 public ActivationOS getOs() {
187 return this.os;
188 }
189
190 /**
191 * Specifies that this profile will be activated when this property is
192 * specified.
193 *
194 * @return a {@code ActivationProperty}
195 */
196 public ActivationProperty getProperty() {
197 return this.property;
198 }
199
200 /**
201 * Specifies that this profile will be activated based on existence of a file.
202 *
203 * @return a {@code ActivationFile}
204 */
205 public ActivationFile getFile() {
206 return this.file;
207 }
208
209 /**
210 * Specifies that this profile will be activated based on the project's packaging.
211 *
212 * @return a {@code String}
213 */
214 public String getPackaging() {
215 return this.packaging;
216 }
217
218 /**
219 * The condition which must be satisfied to activate the profile.
220 *
221 * @return a {@code String}
222 */
223 public String getCondition() {
224 return this.condition;
225 }
226
227 /**
228 * Gets the location of the specified field in the input source.
229 */
230 public InputLocation getLocation(Object key) {
231 return locations.get(key);
232 }
233
234 /**
235 * Gets the keys of the locations of the input source.
236 */
237 public Set<Object> getLocationKeys() {
238 return locations.keySet();
239 }
240
241 protected Stream<Object> getLocationKeyStream() {
242 return locations.keySet().stream();
243 }
244
245 /**
246 * Gets the input location that caused this model to be read.
247 */
248 public InputLocation getImportedFrom()
249 {
250 return importedFrom;
251 }
252
253 /**
254 * Creates a new builder with this object as the basis.
255 *
256 * @return a {@code Builder}
257 */
258 @Nonnull
259 public Builder with() {
260 return newBuilder(this);
261 }
262 /**
263 * Creates a new {@code Activation} instance using the specified activeByDefault.
264 *
265 * @param activeByDefault the new {@code boolean} to use
266 * @return a {@code Activation} with the specified activeByDefault
267 */
268 @Nonnull
269 public Activation withActiveByDefault(boolean activeByDefault) {
270 return newBuilder(this, true).activeByDefault(activeByDefault).build();
271 }
272 /**
273 * Creates a new {@code Activation} instance using the specified jdk.
274 *
275 * @param jdk the new {@code String} to use
276 * @return a {@code Activation} with the specified jdk
277 */
278 @Nonnull
279 public Activation withJdk(String jdk) {
280 return newBuilder(this, true).jdk(jdk).build();
281 }
282 /**
283 * Creates a new {@code Activation} instance using the specified os.
284 *
285 * @param os the new {@code ActivationOS} to use
286 * @return a {@code Activation} with the specified os
287 */
288 @Nonnull
289 public Activation withOs(ActivationOS os) {
290 return newBuilder(this, true).os(os).build();
291 }
292 /**
293 * Creates a new {@code Activation} instance using the specified property.
294 *
295 * @param property the new {@code ActivationProperty} to use
296 * @return a {@code Activation} with the specified property
297 */
298 @Nonnull
299 public Activation withProperty(ActivationProperty property) {
300 return newBuilder(this, true).property(property).build();
301 }
302 /**
303 * Creates a new {@code Activation} instance using the specified file.
304 *
305 * @param file the new {@code ActivationFile} to use
306 * @return a {@code Activation} with the specified file
307 */
308 @Nonnull
309 public Activation withFile(ActivationFile file) {
310 return newBuilder(this, true).file(file).build();
311 }
312 /**
313 * Creates a new {@code Activation} instance using the specified packaging.
314 *
315 * @param packaging the new {@code String} to use
316 * @return a {@code Activation} with the specified packaging
317 */
318 @Nonnull
319 public Activation withPackaging(String packaging) {
320 return newBuilder(this, true).packaging(packaging).build();
321 }
322 /**
323 * Creates a new {@code Activation} instance using the specified condition.
324 *
325 * @param condition the new {@code String} to use
326 * @return a {@code Activation} with the specified condition
327 */
328 @Nonnull
329 public Activation withCondition(String condition) {
330 return newBuilder(this, true).condition(condition).build();
331 }
332
333 /**
334 * Creates a new {@code Activation} instance.
335 * Equivalent to {@code newInstance(true)}.
336 * @see #newInstance(boolean)
337 *
338 * @return a new {@code Activation}
339 */
340 @Nonnull
341 public static Activation newInstance() {
342 return newInstance(true);
343 }
344
345 /**
346 * Creates a new {@code Activation} instance using default values or not.
347 * Equivalent to {@code newBuilder(withDefaults).build()}.
348 *
349 * @param withDefaults the boolean indicating whether default values should be used
350 * @return a new {@code Activation}
351 */
352 @Nonnull
353 public static Activation newInstance(boolean withDefaults) {
354 return newBuilder(withDefaults).build();
355 }
356
357 /**
358 * Creates a new {@code Activation} builder instance.
359 * Equivalent to {@code newBuilder(true)}.
360 * @see #newBuilder(boolean)
361 *
362 * @return a new {@code Builder}
363 */
364 @Nonnull
365 public static Builder newBuilder() {
366 return newBuilder(true);
367 }
368
369 /**
370 * Creates a new {@code Activation} builder instance using default values or not.
371 *
372 * @param withDefaults the boolean indicating whether default values should be used
373 * @return a new {@code Builder}
374 */
375 @Nonnull
376 public static Builder newBuilder(boolean withDefaults) {
377 return new Builder(withDefaults);
378 }
379
380 /**
381 * Creates a new {@code Activation} builder instance using the specified object as a basis.
382 * Equivalent to {@code newBuilder(from, false)}.
383 *
384 * @param from the {@code Activation} instance to use as a basis
385 * @return a new {@code Builder}
386 */
387 @Nonnull
388 public static Builder newBuilder(Activation from) {
389 return newBuilder(from, false);
390 }
391
392 /**
393 * Creates a new {@code Activation} builder instance using the specified object as a basis.
394 *
395 * @param from the {@code Activation} instance to use as a basis
396 * @param forceCopy the boolean indicating if a copy should be forced
397 * @return a new {@code Builder}
398 */
399 @Nonnull
400 public static Builder newBuilder(Activation from, boolean forceCopy) {
401 return new Builder(from, forceCopy);
402 }
403
404 /**
405 * Builder class used to create Activation instances.
406 * @see #with()
407 * @see #newBuilder()
408 */
409 @NotThreadSafe
410 public static class Builder
411 {
412 Activation base;
413 Boolean activeByDefault;
414 String jdk;
415 ActivationOS os;
416 ActivationProperty property;
417 ActivationFile file;
418 String packaging;
419 String condition;
420 Map<Object, InputLocation> locations;
421 InputLocation importedFrom;
422
423 protected Builder(boolean withDefaults) {
424 if (withDefaults) {
425 this.activeByDefault = false;
426 }
427 }
428
429 protected Builder(Activation base, boolean forceCopy) {
430 if (forceCopy) {
431 this.activeByDefault = base.activeByDefault;
432 this.jdk = base.jdk;
433 this.os = base.os;
434 this.property = base.property;
435 this.file = base.file;
436 this.packaging = base.packaging;
437 this.condition = base.condition;
438 this.locations = base.locations;
439 this.importedFrom = base.importedFrom;
440 } else {
441 this.base = base;
442 }
443 }
444
445 @Nonnull
446 public Builder activeByDefault(boolean activeByDefault) {
447 this.activeByDefault = activeByDefault;
448 return this;
449 }
450
451 @Nonnull
452 public Builder jdk(String jdk) {
453 this.jdk = jdk;
454 return this;
455 }
456
457 @Nonnull
458 public Builder os(ActivationOS os) {
459 this.os = os;
460 return this;
461 }
462
463 @Nonnull
464 public Builder property(ActivationProperty property) {
465 this.property = property;
466 return this;
467 }
468
469 @Nonnull
470 public Builder file(ActivationFile file) {
471 this.file = file;
472 return this;
473 }
474
475 @Nonnull
476 public Builder packaging(String packaging) {
477 this.packaging = packaging;
478 return this;
479 }
480
481 @Nonnull
482 public Builder condition(String condition) {
483 this.condition = condition;
484 return this;
485 }
486
487
488 @Nonnull
489 public Builder location(Object key, InputLocation location) {
490 if (location != null) {
491 if (!(this.locations instanceof HashMap)) {
492 this.locations = this.locations != null ? new HashMap<>(this.locations) : new HashMap<>();
493 }
494 this.locations.put(key, location);
495 }
496 return this;
497 }
498
499 @Nonnull
500 public Builder importedFrom(InputLocation importedFrom) {
501 this.importedFrom = importedFrom;
502 return this;
503 }
504
505 @Nonnull
506 public Activation build() {
507 // this method should not contain any logic other than creating (or reusing) an object in order to ease subclassing
508 if (base != null
509 && (activeByDefault == null || activeByDefault == base.activeByDefault)
510 && (jdk == null || jdk == base.jdk)
511 && (os == null || os == base.os)
512 && (property == null || property == base.property)
513 && (file == null || file == base.file)
514 && (packaging == null || packaging == base.packaging)
515 && (condition == null || condition == base.condition)
516 ) {
517 return base;
518 }
519 return new Activation(this);
520 }
521
522 Map<Object, InputLocation> computeLocations() {
523 Map<Object, InputLocation> newlocs = locations != null ? locations : Map.of();
524 Map<Object, InputLocation> oldlocs = base != null ? base.locations : Map.of();
525 if (newlocs.isEmpty()) {
526 return Map.copyOf(oldlocs);
527 }
528 if (oldlocs.isEmpty()) {
529 return Map.copyOf(newlocs);
530 }
531 return Stream.concat(newlocs.entrySet().stream(), oldlocs.entrySet().stream())
532 // Keep value from newlocs in case of duplicates
533 .collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1));
534 }
535 }
536
537 }