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