View Javadoc
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 }