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      * @param key the key of the field, must not be {@code null}
231      * @return the location of the field in the input source or {@code null} if unknown
232      * @throws NullPointerException if {@code key} is {@code null}
233      */
234     public InputLocation getLocation(Object key) {
235         Objects.requireNonNull(key, "key");
236         return locations.get(key);
237     }
238 
239     /**
240      * Gets the keys of the locations of the input source.
241      */
242     public Set<Object> getLocationKeys() {
243         return locations.keySet();
244     }
245 
246     protected Stream<Object> getLocationKeyStream() {
247         return locations.keySet().stream();
248     }
249 
250     /**
251      * Gets the input location that caused this model to be read.
252      */
253     public InputLocation getImportedFrom() {
254         return importedFrom;
255     }
256 
257     /**
258      * Creates a new builder with this object as the basis.
259      *
260      * @return a {@code Builder}
261      */
262     @Nonnull
263     public Builder with() {
264         return newBuilder(this);
265     }
266     /**
267      * Creates a new {@code Activation} instance using the specified activeByDefault.
268      *
269      * @param activeByDefault the new {@code boolean} to use
270      * @return a {@code Activation} with the specified activeByDefault
271      */
272     @Nonnull
273     public Activation withActiveByDefault(boolean activeByDefault) {
274         return newBuilder(this, true).activeByDefault(activeByDefault).build();
275     }
276     /**
277      * Creates a new {@code Activation} instance using the specified jdk.
278      *
279      * @param jdk the new {@code String} to use
280      * @return a {@code Activation} with the specified jdk
281      */
282     @Nonnull
283     public Activation withJdk(String jdk) {
284         return newBuilder(this, true).jdk(jdk).build();
285     }
286     /**
287      * Creates a new {@code Activation} instance using the specified os.
288      *
289      * @param os the new {@code ActivationOS} to use
290      * @return a {@code Activation} with the specified os
291      */
292     @Nonnull
293     public Activation withOs(ActivationOS os) {
294         return newBuilder(this, true).os(os).build();
295     }
296     /**
297      * Creates a new {@code Activation} instance using the specified property.
298      *
299      * @param property the new {@code ActivationProperty} to use
300      * @return a {@code Activation} with the specified property
301      */
302     @Nonnull
303     public Activation withProperty(ActivationProperty property) {
304         return newBuilder(this, true).property(property).build();
305     }
306     /**
307      * Creates a new {@code Activation} instance using the specified file.
308      *
309      * @param file the new {@code ActivationFile} to use
310      * @return a {@code Activation} with the specified file
311      */
312     @Nonnull
313     public Activation withFile(ActivationFile file) {
314         return newBuilder(this, true).file(file).build();
315     }
316     /**
317      * Creates a new {@code Activation} instance using the specified packaging.
318      *
319      * @param packaging the new {@code String} to use
320      * @return a {@code Activation} with the specified packaging
321      */
322     @Nonnull
323     public Activation withPackaging(String packaging) {
324         return newBuilder(this, true).packaging(packaging).build();
325     }
326     /**
327      * Creates a new {@code Activation} instance using the specified condition.
328      *
329      * @param condition the new {@code String} to use
330      * @return a {@code Activation} with the specified condition
331      */
332     @Nonnull
333     public Activation withCondition(String condition) {
334         return newBuilder(this, true).condition(condition).build();
335     }
336 
337     /**
338      * Creates a new {@code Activation} instance.
339      * Equivalent to {@code newInstance(true)}.
340      * @see #newInstance(boolean)
341      *
342      * @return a new {@code Activation}
343      */
344     @Nonnull
345     public static Activation newInstance() {
346         return newInstance(true);
347     }
348 
349     /**
350      * Creates a new {@code Activation} instance using default values or not.
351      * Equivalent to {@code newBuilder(withDefaults).build()}.
352      *
353      * @param withDefaults the boolean indicating whether default values should be used
354      * @return a new {@code Activation}
355      */
356     @Nonnull
357     public static Activation newInstance(boolean withDefaults) {
358         return newBuilder(withDefaults).build();
359     }
360 
361     /**
362      * Creates a new {@code Activation} builder instance.
363      * Equivalent to {@code newBuilder(true)}.
364      * @see #newBuilder(boolean)
365      *
366      * @return a new {@code Builder}
367      */
368     @Nonnull
369     public static Builder newBuilder() {
370         return newBuilder(true);
371     }
372 
373     /**
374      * Creates a new {@code Activation} builder instance using default values or not.
375      *
376      * @param withDefaults the boolean indicating whether default values should be used
377      * @return a new {@code Builder}
378      */
379     @Nonnull
380     public static Builder newBuilder(boolean withDefaults) {
381         return new Builder(withDefaults);
382     }
383 
384     /**
385      * Creates a new {@code Activation} builder instance using the specified object as a basis.
386      * Equivalent to {@code newBuilder(from, false)}.
387      *
388      * @param from the {@code Activation} instance to use as a basis
389      * @return a new {@code Builder}
390      */
391     @Nonnull
392     public static Builder newBuilder(Activation from) {
393         return newBuilder(from, false);
394     }
395 
396     /**
397      * Creates a new {@code Activation} builder instance using the specified object as a basis.
398      *
399      * @param from the {@code Activation} instance to use as a basis
400      * @param forceCopy the boolean indicating if a copy should be forced
401      * @return a new {@code Builder}
402      */
403     @Nonnull
404     public static Builder newBuilder(Activation from, boolean forceCopy) {
405         return new Builder(from, forceCopy);
406     }
407 
408     /**
409      * Builder class used to create Activation instances.
410      * @see #with()
411      * @see #newBuilder()
412      */
413     @NotThreadSafe
414     public static class Builder
415     {
416         Activation base;
417         Boolean activeByDefault;
418         String jdk;
419         ActivationOS os;
420         ActivationProperty property;
421         ActivationFile file;
422         String packaging;
423         String condition;
424         Map<Object, InputLocation> locations;
425         InputLocation importedFrom;
426 
427         protected Builder(boolean withDefaults) {
428             if (withDefaults) {
429                 this.activeByDefault = false;
430             }
431         }
432 
433         protected Builder(Activation base, boolean forceCopy) {
434             if (forceCopy) {
435                 this.activeByDefault = base.activeByDefault;
436                 this.jdk = base.jdk;
437                 this.os = base.os;
438                 this.property = base.property;
439                 this.file = base.file;
440                 this.packaging = base.packaging;
441                 this.condition = base.condition;
442                 this.locations = base.locations;
443                 this.importedFrom = base.importedFrom;
444             } else {
445                 this.base = base;
446             }
447         }
448 
449         @Nonnull
450         public Builder activeByDefault(boolean activeByDefault) {
451             this.activeByDefault = activeByDefault;
452             return this;
453         }
454 
455         @Nonnull
456         public Builder jdk(String jdk) {
457             this.jdk = jdk;
458             return this;
459         }
460 
461         @Nonnull
462         public Builder os(ActivationOS os) {
463             this.os = os;
464             return this;
465         }
466 
467         @Nonnull
468         public Builder property(ActivationProperty property) {
469             this.property = property;
470             return this;
471         }
472 
473         @Nonnull
474         public Builder file(ActivationFile file) {
475             this.file = file;
476             return this;
477         }
478 
479         @Nonnull
480         public Builder packaging(String packaging) {
481             this.packaging = packaging;
482             return this;
483         }
484 
485         @Nonnull
486         public Builder condition(String condition) {
487             this.condition = condition;
488             return this;
489         }
490 
491 
492         @Nonnull
493         public Builder location(Object key, InputLocation location) {
494             if (location != null) {
495                 if (!(this.locations instanceof HashMap)) {
496                     this.locations = this.locations != null ? new HashMap<>(this.locations) : new HashMap<>();
497                 }
498                 this.locations.put(key, location);
499             }
500             return this;
501         }
502 
503         @Nonnull
504         public Builder importedFrom(InputLocation importedFrom) {
505             this.importedFrom = importedFrom;
506             return this;
507         }
508 
509         @Nonnull
510         public Activation build() {
511             // this method should not contain any logic other than creating (or reusing) an object in order to ease subclassing
512             if (base != null
513                     && (activeByDefault == null || activeByDefault == base.activeByDefault)
514                     && (jdk == null || jdk == base.jdk)
515                     && (os == null || os == base.os)
516                     && (property == null || property == base.property)
517                     && (file == null || file == base.file)
518                     && (packaging == null || packaging == base.packaging)
519                     && (condition == null || condition == base.condition)
520             ) {
521                 return base;
522             }
523             return new Activation(this);
524         }
525 
526         Map<Object, InputLocation> computeLocations() {
527             Map<Object, InputLocation> newlocs = locations != null ? locations : Map.of();
528             Map<Object, InputLocation> oldlocs = base != null ? base.locations : Map.of();
529             if (newlocs.isEmpty()) {
530                 return Map.copyOf(oldlocs);
531             }
532             if (oldlocs.isEmpty()) {
533                 return Map.copyOf(newlocs);
534             }
535             return Stream.concat(newlocs.entrySet().stream(), oldlocs.entrySet().stream())
536                     // Keep value from newlocs in case of duplicates
537                     .collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1));
538         }
539     }
540 
541 }