View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.resolver.internal.ant.types;
20  
21  import java.io.File;
22  import java.util.regex.Matcher;
23  import java.util.regex.Pattern;
24  
25  import org.apache.maven.model.Model;
26  import org.apache.maven.resolver.internal.ant.AntRepoSys;
27  import org.apache.maven.resolver.internal.ant.ProjectWorkspaceReader;
28  import org.apache.maven.resolver.internal.ant.tasks.RefTask;
29  import org.apache.tools.ant.BuildException;
30  import org.apache.tools.ant.PropertyHelper;
31  import org.apache.tools.ant.Task;
32  import org.apache.tools.ant.types.Reference;
33  
34  /**
35   * Represents a Maven POM file as an Ant {@code DataType}.
36   * <p>
37   * This type allows an existing {@code pom.xml} file to be loaded into the Ant build environment.
38   * The POM's metadata (such as groupId, artifactId, version, etc.) can be accessed directly or reused
39   * in other Maven Resolver tasks like {@link org.apache.maven.resolver.internal.ant.tasks.Deploy Deploy},
40   * {@link org.apache.maven.resolver.internal.ant.tasks.Install Install}
41   * </p>
42   *
43   * <h2>Usage Example:</h2>
44   * <pre>{@code
45   * <pom file="target/generated-pom.xml" id="my.pom"/>
46   *
47   * <echo message="Group ID is ${pom.groupId}"/>
48   * <echo message="Version is ${pom.version}"/>
49   *
50   * <deploy>
51   *   <pom refid="my.pom"/>
52   *   <repository id="release" url="https://repo.mycompany.com/releases"/>
53   * </deploy>
54   * }</pre>
55   *
56   * <h2>Attributes:</h2>
57   * <ul>
58   *   <li><strong>file</strong> — the path to the {@code pom.xml} file to load</li>
59   * </ul>
60   *
61   * <h2>Exposed Ant Properties:</h2>
62   * When a POM is loaded, the following properties become available for use in your build script:
63   * <ul>
64   *   <li>{@code ${pom.groupId}}</li>
65   *   <li>{@code ${pom.artifactId}}</li>
66   *   <li>{@code ${pom.version}}</li>
67   *   <li>{@code ${pom.packaging}}</li>
68   *   <li>{@code ${pom.name}}</li>
69   *   <li>{@code ${pom.description}} (if present)</li>
70   * </ul>
71   *
72   * These properties enable consistent reuse of project metadata across Ant targets and tasks.
73   * <p>
74   * If you want a different property prefix, you can set it when you register the POM, e.g.:
75   * </p>
76   * <pre>{@code
77   * <pom file="target/generated-pom.xml" id="my.pom" prefix="lib"/>
78   * }</pre>
79   *
80   * <h2>Typical Use Cases:</h2>
81   * <ul>
82   *   <li>Reusing existing Maven coordinates in Ant-based deployments</li>
83   *   <li>Injecting POM metadata into artifact creation or reporting tasks</li>
84   *   <li>Keeping build logic in sync with Maven configuration</li>
85   * </ul>
86   *
87   * @see org.apache.maven.resolver.internal.ant.tasks.Deploy
88   * @see org.apache.maven.resolver.internal.ant.tasks.Install
89   * @see org.apache.maven.resolver.internal.ant.tasks.CreatePom
90   */
91  public class Pom extends RefTask {
92  
93      private Model model;
94  
95      private String id;
96  
97      private File file;
98  
99      private String groupId;
100 
101     private String artifactId;
102 
103     private String version;
104 
105     private String packaging = "jar";
106 
107     private RemoteRepositories remoteRepositories;
108 
109     private String coords;
110 
111     /**
112      * Creates an empty {@code Pom} instance.
113      * <p>
114      * Attributes such as {@code groupId}, {@code artifactId}, and {@code version} can be set individually,
115      * or the POM can be initialized from a {@code file} or via a reference ({@code refid}).
116      * </p>
117      *
118      * <p>
119      * Ant uses this constructor when instantiating the datatype from a build script.
120      * </p>
121      *
122      * @see #setGroupId(String)
123      * @see #setArtifactId(String)
124      * @see #setVersion(String)
125      * @see #setFile(java.io.File)
126      * @see #setRefid(org.apache.tools.ant.types.Reference)
127      */
128     public Pom() {
129         // Default constructor for Ant task
130     }
131 
132     /**
133      * Resolves this object if defined as a reference and verifies that it is a
134      * {@code Pom} instance.
135      *
136      * @return the referenced {@code Pom} instance
137      * @throws org.apache.tools.ant.BuildException if the reference is invalid
138      *
139      * @see #isReference()
140      * @see #getCheckedRef()
141      */
142     protected Pom getRef() {
143         return (Pom) getCheckedRef();
144     }
145 
146     /**
147      * Validates that this POM definition is complete and usable.
148      * <p>
149      * If this object is a reference, validation is delegated to the referenced {@code Pom}.
150      * Otherwise, the method checks that either a {@link #setFile(File) POM file} is provided,
151      * or all of the required Maven coordinates — {@code groupId}, {@code artifactId}, and {@code version} —
152      * are explicitly set.
153      * </p>
154      *
155      * @throws org.apache.tools.ant.BuildException if required attributes are missing
156      *         and no POM file is specified
157      *
158      * @see #isReference()
159      * @see #setFile(File)
160      * @see #setGroupId(String)
161      * @see #setArtifactId(String)
162      * @see #setVersion(String)
163      */
164     public void validate() {
165         if (isReference()) {
166             getRef().validate();
167         } else {
168             if (file == null) {
169                 if (groupId == null) {
170                     throw new BuildException("You must specify the 'groupId' for the POM");
171                 }
172                 if (artifactId == null) {
173                     throw new BuildException("You must specify the 'artifactId' for the POM");
174                 }
175                 if (version == null) {
176                     throw new BuildException("You must specify the 'version' for the POM");
177                 }
178             }
179         }
180     }
181 
182     @Override
183     public void setRefid(Reference ref) {
184         if (id != null || file != null || groupId != null || artifactId != null || version != null) {
185             throw tooManyAttributes();
186         }
187         if (remoteRepositories != null) {
188             throw noChildrenAllowed();
189         }
190         super.setRefid(ref);
191     }
192 
193     /**
194      * Sets the internal identifier for this POM.
195      * <p>
196      * This {@code id} is not the same as Ant's built-in {@code id} attribute, but may be used
197      * internally by the task logic (e.g., for naming, logging, or referencing the POM).
198      * </p>
199      *
200      * <p>
201      * This method must not be used if this object is defined as a reference ({@code refid}),
202      * or if attributes are otherwise disallowed in the current context.
203      * </p>
204      *
205      * @param id the internal identifier to assign
206      *
207      * @throws org.apache.tools.ant.BuildException if attribute usage is disallowed
208      *
209      * @see #isReference()
210      */
211     public void setId(String id) {
212         checkAttributesAllowed();
213         this.id = id;
214     }
215 
216     /**
217      * Returns the file that was set via {@link #setFile(File)} to load the POM from.
218      * <p>
219      * If a file was specified, it is used as the source for reading the POM’s metadata
220      * instead of defining coordinates manually. This is useful when you want to reference
221      * an existing {@code pom.xml} file directly.
222      * </p>
223      *
224      * <p>
225      * If this {@code Pom} is defined as a reference ({@code refid}),
226      * the file is retrieved from the referenced {@code Pom} instance.
227      * </p>
228      *
229      * @return the POM file, or {@code null} if not set
230      *
231      * @see #setFile(File)
232      * @see #isReference()
233      */
234     public File getFile() {
235         if (isReference()) {
236             return getRef().getFile();
237         }
238         return file;
239     }
240 
241     /**
242      * Sets the file from which to load the POM.
243      * <p>
244      * This is an alternative to specifying Maven coordinates manually. When set,
245      * the POM's metadata is loaded directly from the given file.
246      * </p>
247      *
248      * <p>This method must not be used in combination with:</p>
249      * <ul>
250      *   <li>{@link #setGroupId(String)}, {@link #setArtifactId(String)}, {@link #setVersion(String)}, or {@link #setCoords(String)}</li>
251      *   <li>or if this object is defined as a {@code refid}</li>
252      * </ul>
253      * <p>
254      * Doing so will raise a {@link org.apache.tools.ant.BuildException}.
255      * </p>
256      *
257      * @param file the file containing the POM
258      *
259      * @throws org.apache.tools.ant.BuildException if attributes are in conflict or not allowed
260      *
261      * @see #getFile()
262      * @see #setCoords(String)
263      * @see #isReference()
264      */
265     public void setFile(File file) {
266         checkAttributesAllowed();
267         if (groupId != null || artifactId != null || version != null) {
268             throw ambiguousSource();
269         }
270 
271         this.file = file;
272     }
273 
274     /**
275      * Returns the {@code groupId} of the POM.
276      * <p>
277      * The group ID uniquely identifies the organization or project to which the artifact belongs,
278      * such as {@code org.apache.maven}. This value is typically required when manually specifying
279      * Maven coordinates using {@link #setGroupId(String)} or {@link #setCoords(String)}.
280      * </p>
281      *
282      * <p>
283      * If this {@code Pom} is defined as a reference ({@code refid}),
284      * the value is delegated to the referenced {@code Pom} instance.
285      * </p>
286      *
287      * @return the group ID, or {@code null} if not set
288      *
289      * @see #setGroupId(String)
290      * @see #isReference()
291      */
292     public String getGroupId() {
293         if (isReference()) {
294             return getRef().getGroupId();
295         }
296         return groupId;
297     }
298 
299     /**
300      * Sets the {@code groupId} for the POM.
301      * <p>
302      * The group ID typically represents the project's organization or domain,
303      * such as {@code org.apache.maven} or {@code com.example}.
304      * This value is required when specifying coordinates manually.
305      * </p>
306      *
307      * <p>This method must not be called if:</p>
308      * <ul>
309      *   <li>{@code groupId} has already been set (directly or via {@link #setCoords(String)}),</li>
310      *   <li>or if a POM file has been specified via {@link #setFile(java.io.File)},</li>
311      *   <li>or if this object is defined via a {@code refid}.</li>
312      * </ul>
313      * <p>
314      * Violations will result in a {@link org.apache.tools.ant.BuildException}.
315      * </p>
316      *
317      * @param groupId the Maven group ID
318      *
319      * @throws org.apache.tools.ant.BuildException if attributes are in conflict or not allowed
320      *
321      * @see #getGroupId()
322      * @see #setArtifactId(String)
323      * @see #setVersion(String)
324      * @see #setCoords(String)
325      * @see #setFile(java.io.File)
326      * @see #isReference()
327      */
328     public void setGroupId(String groupId) {
329         checkAttributesAllowed();
330         if (this.groupId != null) {
331             throw ambiguousCoords();
332         }
333         if (file != null) {
334             throw ambiguousSource();
335         }
336         this.groupId = groupId;
337     }
338 
339     /**
340      * Returns the {@code artifactId} of the POM.
341      * <p>
342      * The artifact ID is the name that uniquely identifies the project within its group,
343      * such as {@code maven-resolver-ant-tasks}.
344      * </p>
345      *
346      * <p>
347      * If this {@code Pom} is defined as a reference ({@code refid}),
348      * the value is retrieved from the referenced instance.
349      * </p>
350      *
351      * @return the artifact ID, or {@code null} if not set
352      *
353      * @see #setArtifactId(String)
354      * @see #isReference()
355      */
356     public String getArtifactId() {
357         if (isReference()) {
358             return getRef().getArtifactId();
359         }
360         return artifactId;
361     }
362 
363     /**
364      * Sets the {@code artifactId} for the POM.
365      * <p>
366      * The artifact ID identifies the project within its group, for example: {@code guice}, {@code junit}, or {@code my-module}.
367      * This value is required when manually specifying the Maven coordinates (groupId, artifactId, version).
368      * </p>
369      *
370      * <p>This method must not be called if:</p>
371      * <ul>
372      *   <li>{@code artifactId} was already set (directly or via {@link #setCoords(String)}),</li>
373      *   <li>or if a {@code file} was specified via {@link #setFile(java.io.File)},</li>
374      *   <li>or if this instance is a reference ({@code refid}).</li>
375      * </ul>
376      * <p>
377      * Violating these constraints will result in a {@link org.apache.tools.ant.BuildException}.
378      * </p>
379      *
380      * @param artifactId the Maven artifact ID
381      *
382      * @throws org.apache.tools.ant.BuildException if attributes are in conflict or not allowed
383      *
384      * @see #getArtifactId()
385      * @see #setGroupId(String)
386      * @see #setVersion(String)
387      * @see #setCoords(String)
388      * @see #setFile(java.io.File)
389      * @see #isReference()
390      */
391     public void setArtifactId(String artifactId) {
392         checkAttributesAllowed();
393         if (this.artifactId != null) {
394             throw ambiguousCoords();
395         }
396         if (file != null) {
397             throw ambiguousSource();
398         }
399         this.artifactId = artifactId;
400     }
401 
402     /**
403      * Returns the {@code version} of the Maven artifact defined by this POM.
404      * <p>
405      * The version is one of the required coordinates for uniquely identifying a Maven artifact.
406      * If this object is defined as a reference ({@code refid}), the version is retrieved from the referenced instance.
407      * </p>
408      *
409      * <p>
410      * If this POM is resolved from a file and uses a {@code dependencyManagement} section (e.g. via a BOM),
411      * the version may be inherited and not explicitly required here.
412      * </p>
413      *
414      * @return the version of the artifact, or {@code null} if not set and not resolved
415      *
416      * @see #setVersion(String)
417      * @see #isReference()
418      */
419     public String getVersion() {
420         if (isReference()) {
421             return getRef().getVersion();
422         }
423         return version;
424     }
425 
426     /**
427      * Sets the {@code version} of the POM.
428      * <p>
429      * This value defines the version of the project artifact being referenced,
430      * such as {@code 1.0.0} or {@code 2.5-SNAPSHOT}.
431      * </p>
432      *
433      * <p>This method must not be used if:</p>
434      * <ul>
435      *   <li>{@code version} is already set (directly or via {@link #setCoords(String)}),</li>
436      *   <li>a POM file is specified via {@link #setFile(java.io.File)},</li>
437      *   <li>or this instance is defined as a {@code refid}.</li>
438      * </ul>
439      * <p>
440      * Violating these constraints will cause a {@link org.apache.tools.ant.BuildException}.
441      * </p>
442      *
443      * @param version the Maven version
444      *
445      * @throws org.apache.tools.ant.BuildException if attributes are in conflict or not allowed
446      *
447      * @see #getVersion()
448      * @see #setGroupId(String)
449      * @see #setArtifactId(String)
450      * @see #setCoords(String)
451      * @see #setFile(java.io.File)
452      * @see #isReference()
453      */
454     public void setVersion(String version) {
455         checkAttributesAllowed();
456         if (this.version != null) {
457             throw ambiguousCoords();
458         }
459         if (file != null) {
460             throw ambiguousSource();
461         }
462         this.version = version;
463     }
464 
465     /**
466      * Returns the raw Maven coordinates string that was set via {@link #setCoords(String)}.
467      * <p>
468      * This string is typically in the format {@code groupId:artifactId:version}, but may also include
469      * optional parts such as {@code :extension} or {@code :classifier} depending on the usage context.
470      * </p>
471      *
472      * <p>
473      * If this {@code Pom} is defined as a reference ({@code refid}), the value is retrieved
474      * from the referenced instance.
475      * </p>
476      *
477      * @return the raw coordinate string, or {@code null} if not set
478      *
479      * @see #setCoords(String)
480      * @see #isReference()
481      */
482     public String getCoords() {
483         if (isReference()) {
484             return getRef().getCoords();
485         }
486         return coords;
487     }
488 
489     /**
490      * Sets the Maven coordinates for this POM using a compact string format.
491      * <p>
492      * The expected format is {@code groupId:artifactId:version}, where:
493      * </p>
494      * <ul>
495      *   <li>{@code groupId} is the group ID of the artifact, e.g. {@code org.apache.maven}</li>
496      *   <li>{@code artifactId} is the artifact ID, e.g. {@code maven-core}</li>
497      *   <li>{@code version} is the version, e.g. {@code 3.9.0}</li>
498      * </ul>
499      *
500      * <p>
501      * This method provides a concise alternative to setting each of {@link #setGroupId(String)},
502      * {@link #setArtifactId(String)}, and {@link #setVersion(String)} individually.
503      * </p>
504      *
505      * <p><b>Mutual exclusion:</b> This method must not be used in combination with:</p>
506      * <ul>
507      *   <li>{@link #setGroupId(String)}, {@link #setArtifactId(String)}, or {@link #setVersion(String)}</li>
508      *   <li>{@link #setFile(java.io.File)}</li>
509      *   <li>{@link #setRefid(org.apache.tools.ant.types.Reference)}</li>
510      * </ul>
511      * Violating this constraint will result in a {@link org.apache.tools.ant.BuildException}.
512      *
513      * @param coords the Maven coordinates in the format {@code groupId:artifactId:version}
514      *
515      * @throws org.apache.tools.ant.BuildException if the format is invalid or attributes conflict
516      *
517      * @see #getCoords()
518      * @see #setGroupId(String)
519      * @see #setArtifactId(String)
520      * @see #setVersion(String)
521      * @see #setFile(java.io.File)
522      * @see #setRefid(org.apache.tools.ant.types.Reference)
523      */
524     public void setCoords(String coords) {
525         checkAttributesAllowed();
526         if (file != null) {
527             throw ambiguousSource();
528         }
529         if (groupId != null || artifactId != null || version != null) {
530             throw ambiguousCoords();
531         }
532         Pattern p = Pattern.compile("([^: ]+):([^: ]+):([^: ]+)");
533         Matcher m = p.matcher(coords);
534         if (!m.matches()) {
535             throw new BuildException("Bad POM coordinates, expected format is <groupId>:<artifactId>:<version>");
536         }
537         groupId = m.group(1);
538         artifactId = m.group(2);
539         version = m.group(3);
540     }
541 
542     private BuildException ambiguousCoords() {
543         return new BuildException("You must not specify both 'coords' and ('groupId', 'artifactId', 'version')");
544     }
545 
546     private BuildException ambiguousSource() {
547         return new BuildException(
548                 "You must not specify both 'file' and " + "('coords', 'groupId', 'artifactId', 'version')");
549     }
550 
551     /**
552      * Returns the packaging type of the POM, such as {@code jar}, {@code pom}, or {@code war}.
553      * <p>
554      * This value is typically set via {@link #setPackaging(String)} or resolved from a parsed POM file.
555      * If not explicitly set, the default packaging is usually {@code jar}.
556      * </p>
557      *
558      * <p>
559      * If this {@code Pom} is defined as a reference ({@code refid}),
560      * the packaging is retrieved from the referenced {@code Pom} instance.
561      * </p>
562      *
563      * @return the packaging type, or {@code null} if not set
564      *
565      * @see #setPackaging(String)
566      * @see #isReference()
567      */
568     public String getPackaging() {
569         if (isReference()) {
570             return getRef().getPackaging();
571         }
572         return packaging;
573     }
574 
575     /**
576      * Sets the packaging type of the POM, such as {@code jar}, {@code war}, {@code pom}, etc.
577      * <p>
578      * This value determines how the artifact is packaged and what build lifecycle it uses.
579      * If not specified, Maven assumes a default of {@code jar}.
580      * </p>
581      *
582      * <p>
583      * This method must not be used if a POM file has been specified via {@link #setFile(java.io.File)},
584      * or if this object is defined as a reference ({@code refid}). Violating these constraints
585      * will result in a {@link org.apache.tools.ant.BuildException}.
586      * </p>
587      *
588      * @param packaging the packaging type to assign to the POM
589      *
590      * @throws org.apache.tools.ant.BuildException if attributes are in conflict or not allowed
591      *
592      * @see #getPackaging()
593      * @see #setFile(java.io.File)
594      * @see #isReference()
595      */
596     public void setPackaging(String packaging) {
597         checkAttributesAllowed();
598         if (file != null) {
599             throw ambiguousSource();
600         }
601         this.packaging = packaging;
602     }
603 
604     /**
605      * Returns the internal {@link RemoteRepositories} container associated with this POM.
606      * <p>
607      * This container holds the list of remote repositories that may be used when resolving
608      * the POM from a file, including parent POMs, dependencies, and plugin artifacts.
609      * </p>
610      *
611      * <p>
612      * If the container has not been initialized yet, this method will lazily create and return it.
613      * </p>
614      *
615      * @return the {@code RemoteRepositories} instance associated with this POM
616      *
617      * @see #addRemoteRepos(RemoteRepositories)
618      * @see RemoteRepository
619      */
620     private RemoteRepositories getRemoteRepos() {
621         if (remoteRepositories == null) {
622             remoteRepositories = new RemoteRepositories();
623             remoteRepositories.setProject(getProject());
624         }
625         return remoteRepositories;
626     }
627 
628     /**
629      * Adds a single remote repository to this POM's list of repositories.
630      * <p>
631      * These repositories are used when resolving artifacts related to this POM,
632      * such as parent POMs or dependencies, especially when the POM is loaded from a file.
633      * </p>
634      *
635      * <p>
636      * This method delegates to {@link #getRemoteRepos()} and appends the given
637      * {@link RemoteRepository} to the internal {@link RemoteRepositories} container.
638      * </p>
639      *
640      * @param repository the remote repository to add
641      *
642      * @see #getRemoteRepos()
643      * @see RemoteRepository
644      * @see RemoteRepositories
645      */
646     public void addRemoteRepo(RemoteRepository repository) {
647         getRemoteRepos().addRemoterepo(repository);
648     }
649 
650     /**
651      * Adds a {@link RemoteRepositories} container to this POM, which holds one or more remote repositories
652      * used to resolve dependencies and parent POMs when the POM is loaded from a file.
653      * <p>
654      * This method delegates to {@link #getRemoteRepos()} and appends the given repository container
655      * to the list of remote repositories associated with this POM.
656      * </p>
657      *
658      * @param repositories a container of remote repositories to add
659      *
660      * @see #getRemoteRepos()
661      * @see RemoteRepositories#addRemoterepos(RemoteRepositories)
662      * @see RemoteRepository
663      */
664     public void addRemoteRepos(RemoteRepositories repositories) {
665         getRemoteRepos().addRemoterepos(repositories);
666     }
667 
668     /**
669      * Adds a reference to a {@link RemoteRepositories} instance to this POM's list of remote repositories.
670      * <p>
671      * This allows remote repositories to be defined elsewhere in the build script and referenced by ID,
672      * promoting reuse and separation of concerns.
673      * </p>
674      *
675      * <p>
676      * Internally, this method creates a new {@code RemoteRepositories} object, sets the reference ID,
677      * and delegates to {@link #getRemoteRepos()} to append it to the list.
678      * </p>
679      *
680      * @param ref the Ant {@code Reference} to a {@code RemoteRepositories} definition
681      *
682      * @see RemoteRepositories
683      * @see #addRemoteRepos(RemoteRepositories)
684      * @see #getRemoteRepos()
685      */
686     public void setRemoteReposRef(Reference ref) {
687         RemoteRepositories repos = new RemoteRepositories();
688         repos.setProject(getProject());
689         repos.setRefid(ref);
690         getRemoteRepos().addRemoterepos(repos);
691     }
692 
693     /**
694      * Returns the parsed Maven {@link org.apache.maven.model.Model} associated with this POM.
695      * <p>
696      * If a {@code file} has been specified via {@link #setFile(java.io.File)},
697      * the model is lazily loaded and cached using the {@link AntRepoSys} instance
698      * for the current Ant {@link org.apache.tools.ant.Project}. If no file is set,
699      * this method returns {@code null}.
700      * </p>
701      *
702      * <p>
703      * If this {@code Pom} is defined as a reference ({@code refid}),
704      * the model is retrieved from the referenced {@code Pom} instance.
705      * </p>
706      *
707      * <p>
708      * This method is thread-safe and performs lazy loading: the model is loaded only once
709      * and reused on subsequent calls.
710      * </p>
711      *
712      * @param task the Ant task context used for logging and error reporting during model loading
713      * @return the Maven {@code Model} parsed from the specified POM file, or {@code null} if no file was set
714      *
715      * @see #setFile(java.io.File)
716      * @see #isReference()
717      * @see org.apache.maven.model.Model
718      */
719     public Model getModel(Task task) {
720         if (isReference()) {
721             return getRef().getModel(task);
722         }
723         synchronized (this) {
724             if (model == null) {
725                 if (file != null) {
726                     model = AntRepoSys.getInstance(getProject()).loadModel(task, file, true, remoteRepositories);
727                 }
728             }
729             return model;
730         }
731     }
732 
733     @Override
734     public void execute() {
735         validate();
736 
737         if (file != null && (id == null || AntRepoSys.getInstance(getProject()).getDefaultPom() == null)) {
738             AntRepoSys.getInstance(getProject()).setDefaultPom(this);
739         }
740 
741         ProjectWorkspaceReader.getInstance().addPom(this);
742 
743         Model model = getModel(this);
744 
745         if (model == null) {
746             coords = getGroupId() + ":" + getArtifactId() + ":" + getVersion();
747             return;
748         }
749 
750         coords = model.getGroupId() + ":" + model.getArtifactId() + ":" + model.getVersion();
751 
752         ModelValueExtractor extractor = new ModelValueExtractor(id, model, getProject());
753 
754         PropertyHelper propHelper = PropertyHelper.getPropertyHelper(getProject());
755 
756         try {
757             // Ant 1.8.0 delegate
758             PomPropertyEvaluator.register(extractor, propHelper);
759         } catch (LinkageError e) {
760             // Ant 1.6 - 1.7.1 interceptor chaining
761             PomPropertyHelper.register(extractor, propHelper);
762         }
763     }
764 
765     @Override
766     public String toString() {
767         return coords + " (" + super.toString() + ")";
768     }
769 }