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.model.building;
20  
21  import javax.inject.Named;
22  import javax.inject.Singleton;
23  
24  import java.nio.file.Files;
25  import java.nio.file.Path;
26  import java.nio.file.Paths;
27  import java.util.Arrays;
28  import java.util.Objects;
29  import java.util.Optional;
30  
31  import org.apache.maven.model.Dependency;
32  import org.apache.maven.model.Model;
33  import org.apache.maven.model.Parent;
34  
35  /**
36   * ModelSourceTransformer for the build pom
37   *
38   * @since 4.0.0
39   */
40  @Named
41  @Singleton
42  class BuildModelSourceTransformer implements ModelSourceTransformer {
43  
44      public static final String NAMESPACE_PREFIX = "http://maven.apache.org/POM/";
45  
46      @Override
47      public void transform(Path pomFile, TransformerContext context, Model model) {
48          handleModelVersion(model);
49          handleParent(pomFile, context, model);
50          handleReactorDependencies(context, model);
51          handleCiFriendlyVersion(context, model);
52      }
53  
54      //
55      // Infer modelVersion from namespace URI
56      //
57      void handleModelVersion(Model model) {
58          String namespace = model.getDelegate().getNamespaceUri();
59          if (model.getModelVersion() == null && namespace != null && namespace.startsWith(NAMESPACE_PREFIX)) {
60              model.setModelVersion(namespace.substring(NAMESPACE_PREFIX.length()));
61          }
62      }
63  
64      //
65      // Infer parent information
66      //
67      void handleParent(Path pomFile, TransformerContext context, Model model) {
68          Parent parent = model.getParent();
69          if (parent != null) {
70              String version = parent.getVersion();
71              String path = Optional.ofNullable(parent.getRelativePath()).orElse("..");
72              if (version == null && !path.isEmpty()) {
73                  Optional<RelativeProject> resolvedParent = resolveRelativePath(
74                          pomFile, context, Paths.get(path), parent.getGroupId(), parent.getArtifactId());
75                  resolvedParent.ifPresent(relativeProject -> parent.setVersion(relativeProject.getVersion()));
76              }
77          }
78      }
79  
80      //
81      // CI friendly versions
82      //
83      void handleCiFriendlyVersion(TransformerContext context, Model model) {
84          String version = model.getVersion();
85          String modVersion = replaceCiFriendlyVersion(context, version);
86          model.setVersion(modVersion);
87  
88          Parent parent = model.getParent();
89          if (parent != null) {
90              version = parent.getVersion();
91              modVersion = replaceCiFriendlyVersion(context, version);
92              parent.setVersion(modVersion);
93          }
94      }
95  
96      //
97      // Infer inner reactor dependencies version
98      //
99      void handleReactorDependencies(TransformerContext context, Model model) {
100         for (Dependency dep : model.getDependencies()) {
101             if (dep.getVersion() == null) {
102                 Model depModel =
103                         context.getRawModel(model.getDelegate().getPomFile(), dep.getGroupId(), dep.getArtifactId());
104                 if (depModel != null) {
105                     String v = depModel.getVersion();
106                     if (v == null && depModel.getParent() != null) {
107                         v = depModel.getParent().getVersion();
108                     }
109                     dep.setVersion(v);
110                 }
111             }
112         }
113     }
114 
115     protected String replaceCiFriendlyVersion(TransformerContext context, String version) {
116         if (version != null) {
117             for (String key : Arrays.asList("changelist", "revision", "sha1")) {
118                 String val = context.getUserProperty(key);
119                 if (val != null) {
120                     version = version.replace("${" + key + "}", val);
121                 }
122             }
123         }
124         return version;
125     }
126 
127     protected Optional<RelativeProject> resolveRelativePath(
128             Path pomFile, TransformerContext context, Path relativePath, String groupId, String artifactId) {
129         Path pomPath = pomFile.resolveSibling(relativePath).normalize();
130         if (Files.isDirectory(pomPath)) {
131             pomPath = context.locate(pomPath);
132         }
133 
134         if (pomPath == null || !Files.isRegularFile(pomPath)) {
135             return Optional.empty();
136         }
137 
138         Optional<RelativeProject> mappedProject = Optional.ofNullable(context.getRawModel(pomFile, pomPath.normalize()))
139                 .map(BuildModelSourceTransformer::toRelativeProject);
140 
141         if (mappedProject.isPresent()) {
142             RelativeProject project = mappedProject.get();
143 
144             if (Objects.equals(groupId, project.getGroupId()) && Objects.equals(artifactId, project.getArtifactId())) {
145                 return mappedProject;
146             }
147         }
148         return Optional.empty();
149     }
150 
151     private static RelativeProject toRelativeProject(final org.apache.maven.model.Model m) {
152         String groupId = m.getGroupId();
153         if (groupId == null && m.getParent() != null) {
154             groupId = m.getParent().getGroupId();
155         }
156 
157         String version = m.getVersion();
158         if (version == null && m.getParent() != null) {
159             version = m.getParent().getVersion();
160         }
161 
162         return new RelativeProject(groupId, m.getArtifactId(), version);
163     }
164 
165     static class RelativeProject {
166         private final String groupId;
167 
168         private final String artifactId;
169 
170         private final String version;
171 
172         RelativeProject(String groupId, String artifactId, String version) {
173             this.groupId = groupId;
174             this.artifactId = artifactId;
175             this.version = version;
176         }
177 
178         public String getGroupId() {
179             return groupId;
180         }
181 
182         public String getArtifactId() {
183             return artifactId;
184         }
185 
186         public String getVersion() {
187             return version;
188         }
189     }
190 }