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.Path;
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.Objects;
25  import java.util.function.Function;
26  
27  import org.apache.maven.api.di.Inject;
28  import org.apache.maven.api.di.Named;
29  import org.apache.maven.api.di.Singleton;
30  import org.apache.maven.api.model.Build;
31  import org.apache.maven.api.model.Model;
32  import org.apache.maven.api.model.Reporting;
33  import org.apache.maven.api.model.Resource;
34  import org.apache.maven.api.services.ModelBuilderRequest;
35  import org.apache.maven.api.services.model.ModelPathTranslator;
36  import org.apache.maven.api.services.model.PathTranslator;
37  
38  /**
39   * Resolves relative paths within a model against a specific base directory.
40   *
41   */
42  @Named
43  @Singleton
44  public class DefaultModelPathTranslator implements ModelPathTranslator {
45  
46      private final PathTranslator pathTranslator;
47  
48      @Inject
49      public DefaultModelPathTranslator(PathTranslator pathTranslator) {
50          this.pathTranslator = pathTranslator;
51      }
52  
53      @Override
54      public Model alignToBaseDirectory(Model model, Path basedir, ModelBuilderRequest request) {
55          if (model == null || basedir == null) {
56              return model;
57          }
58  
59          Build build = model.getBuild();
60          Build newBuild = null;
61          if (build != null) {
62              newBuild = Build.newBuilder(build)
63                      .directory(alignToBaseDirectory(build.getDirectory(), basedir))
64                      .sourceDirectory(alignToBaseDirectory(build.getSourceDirectory(), basedir))
65                      .testSourceDirectory(alignToBaseDirectory(build.getTestSourceDirectory(), basedir))
66                      .scriptSourceDirectory(alignToBaseDirectory(build.getScriptSourceDirectory(), basedir))
67                      .resources(map(build.getResources(), r -> alignToBaseDirectory(r, basedir)))
68                      .testResources(map(build.getTestResources(), r -> alignToBaseDirectory(r, basedir)))
69                      .filters(map(build.getFilters(), s -> alignToBaseDirectory(s, basedir)))
70                      .outputDirectory(alignToBaseDirectory(build.getOutputDirectory(), basedir))
71                      .testOutputDirectory(alignToBaseDirectory(build.getTestOutputDirectory(), basedir))
72                      .build();
73          }
74  
75          Reporting reporting = model.getReporting();
76          Reporting newReporting = null;
77          if (reporting != null) {
78              newReporting = Reporting.newBuilder(reporting)
79                      .outputDirectory(alignToBaseDirectory(reporting.getOutputDirectory(), basedir))
80                      .build();
81          }
82          if (newBuild != build || newReporting != reporting) {
83              model = Model.newBuilder(model)
84                      .build(newBuild)
85                      .reporting(newReporting)
86                      .build();
87          }
88          return model;
89      }
90  
91      private <T> List<T> map(List<T> resources, Function<T, T> mapper) {
92          List<T> newResources = null;
93          if (resources != null) {
94              for (int i = 0; i < resources.size(); i++) {
95                  T resource = resources.get(i);
96                  T newResource = mapper.apply(resource);
97                  if (newResource != resource) {
98                      if (newResources == null) {
99                          newResources = new ArrayList<>(resources);
100                     }
101                     newResources.set(i, newResource);
102                 }
103             }
104         }
105         return newResources;
106     }
107 
108     /**
109      * Returns a resource with all properties identical to the given resource, except the paths
110      * which are resolved according the given {@code basedir}. If the paths are unchanged, then
111      * this method returns the previous instance.
112      *
113      * @param resource the resource to relocate, or {@code null}
114      * @param basedir the new base directory
115      * @return relocated resource, or {@code null} if the given resource was null
116      */
117     @SuppressWarnings("StringEquality") // Identity comparison is ok in this method.
118     private Resource alignToBaseDirectory(Resource resource, Path basedir) {
119         if (resource != null) {
120             String oldDir = resource.getDirectory();
121             String newDir = alignToBaseDirectory(oldDir, basedir);
122             if (newDir != oldDir) {
123                 return resource.with().directory(newDir).build();
124             }
125         }
126         return resource;
127     }
128 
129     /**
130      * Returns a path relocated to the given base directory. If the result of this operation
131      * is the same path as before, then this method returns the old {@code path} instance.
132      * It is okay for the caller to compare the {@link String} instances using the identity
133      * comparator for detecting changes.
134      *
135      * @param path the path to relocate, or {@code null}
136      * @param basedir the new base directory
137      * @return relocated path, or {@code null} if the given path was null
138      */
139     private String alignToBaseDirectory(String path, Path basedir) {
140         String newPath = pathTranslator.alignToBaseDirectory(path, basedir);
141         return Objects.equals(path, newPath) ? path : newPath;
142     }
143 }