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.shared.release.util;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.Reader;
24  import java.nio.file.Path;
25  import java.nio.file.Paths;
26  import java.util.Arrays;
27  import java.util.List;
28  
29  import org.apache.maven.artifact.ArtifactUtils;
30  import org.apache.maven.model.Model;
31  import org.apache.maven.project.MavenProject;
32  import org.apache.maven.shared.release.ReleaseExecutionException;
33  import org.apache.maven.shared.release.config.ReleaseDescriptor;
34  import org.apache.maven.shared.release.config.ReleaseDescriptorBuilder;
35  import org.apache.maven.shared.release.config.ReleaseUtils;
36  import org.codehaus.plexus.interpolation.InterpolationException;
37  import org.codehaus.plexus.interpolation.MapBasedValueSource;
38  import org.codehaus.plexus.interpolation.ObjectBasedValueSource;
39  import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
40  import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
41  import org.codehaus.plexus.interpolation.StringSearchInterpolator;
42  import org.codehaus.plexus.util.IOUtil;
43  import org.codehaus.plexus.util.ReaderFactory;
44  
45  /**
46   * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
47   */
48  public class ReleaseUtil {
49      @SuppressWarnings("checkstyle:constantname")
50      public static final String RELEASE_POMv4 = "release-pom.xml";
51  
52      @SuppressWarnings("checkstyle:constantname")
53      public static final String POMv4 = "pom.xml";
54  
55      /**
56       * The line separator to use.
57       */
58      public static final String LS = System.getProperty("line.separator");
59  
60      private ReleaseUtil() {
61          // noop
62      }
63  
64      public static MavenProject getRootProject(List<MavenProject> reactorProjects) {
65          MavenProject project = reactorProjects.get(0);
66          for (MavenProject currentProject : reactorProjects) {
67              if (currentProject.isExecutionRoot()) {
68                  project = currentProject;
69                  break;
70              }
71          }
72  
73          return project;
74      }
75  
76      public static File getStandardPom(MavenProject project) {
77          if (project == null) {
78              return null;
79          }
80  
81          File pom = project.getFile();
82  
83          if (pom == null) {
84              return null;
85          }
86  
87          File releasePom = getReleasePom(project);
88          if (pom.equals(releasePom)) {
89              pom = new File(pom.getParent(), POMv4);
90          }
91  
92          return pom;
93      }
94  
95      public static File getReleasePom(MavenProject project) {
96          if (project == null) {
97              return null;
98          }
99  
100         File pom = project.getFile();
101 
102         if (pom == null) {
103             return null;
104         }
105 
106         return new File(pom.getParent(), RELEASE_POMv4);
107     }
108 
109     /**
110      * Gets the string contents of the specified XML file. Note: In contrast to an XML processor, the line separators in
111      * the returned string will be normalized to use the platform's native line separator. This is basically to save
112      * another normalization step when writing the string contents back to an XML file.
113      *
114      * @param file the path to the XML file to read in, must not be <code>null</code>
115      * @return the string contents of the XML file
116      * @throws IOException if the file could not be opened/read
117      */
118     public static String readXmlFile(File file) throws IOException {
119         return readXmlFile(file, LS);
120     }
121 
122     public static String readXmlFile(File file, String ls) throws IOException {
123         try (Reader reader = ReaderFactory.newXmlReader(file)) {
124             return normalizeLineEndings(IOUtil.toString(reader), ls);
125         }
126     }
127 
128     /**
129      * Normalizes the line separators in the specified string.
130      *
131      * @param text the string to normalize, may be <code>null</code>
132      * @param separator the line separator to use for normalization, typically "\n" or "\r\n", must not be
133      *            <code>null</code>
134      * @return the input string with normalized line separators or <code>null</code> if the string was <code>null</code>
135      *
136      */
137     public static String normalizeLineEndings(String text, String separator) {
138         String norm = text;
139         if (text != null) {
140             norm = text.replaceAll("(\r\n)|(\n)|(\r)", separator);
141         }
142         return norm;
143     }
144 
145     public static ReleaseDescriptor createBasedirAlignedReleaseDescriptor(
146             ReleaseDescriptor releaseDescriptor, List<MavenProject> reactorProjects) throws ReleaseExecutionException {
147         int parentLevels = Paths.get(releaseDescriptor.getPomFileName()).getNameCount() - 1;
148 
149         String url = releaseDescriptor.getScmSourceUrl();
150         url = realignScmUrl(parentLevels, url);
151 
152         ReleaseDescriptorBuilder builder = new ReleaseDescriptorBuilder();
153         builder.setWorkingDirectory(releaseDescriptor.getWorkingDirectory());
154         builder.setScmSourceUrl(url);
155 
156         return ReleaseUtils.buildReleaseDescriptor(builder);
157     }
158 
159     public static int getBaseWorkingDirectoryParentCount(final Path baseDirectory, final Path workingDirectory) {
160         return Math.max(
161                 0,
162                 workingDirectory.normalize().getNameCount()
163                         - baseDirectory.normalize().getNameCount());
164     }
165 
166     public static String realignScmUrl(int parentLevels, String url) {
167         if (!(url == null || url.isEmpty())) {
168             // normalize
169             url = url.replaceAll("/\\./", "/")
170                     .replaceAll("/\\.$", "")
171                     .replaceAll("/[^/]+/\\.\\./", "/")
172                     .replaceAll("/[^/]+/\\.\\.$", "");
173 
174             int index = url.length();
175             String suffix = "";
176             if (url.endsWith("/")) {
177                 index--;
178                 suffix = "/";
179             }
180 
181             for (int i = 0; i < parentLevels && index > 0; i++) {
182                 index = url.lastIndexOf('/', index - 1);
183             }
184 
185             if (index > 0) {
186                 url = url.substring(0, index) + suffix;
187             }
188         }
189         return url;
190     }
191 
192     public static String interpolate(String value, Model model) throws ReleaseExecutionException {
193         if (value != null && value.contains("${")) {
194             StringSearchInterpolator interpolator = new StringSearchInterpolator();
195             List<String> pomPrefixes = Arrays.asList("pom.", "project.");
196             interpolator.addValueSource(new PrefixedObjectValueSource(pomPrefixes, model, false));
197             interpolator.addValueSource(new MapBasedValueSource(model.getProperties()));
198             interpolator.addValueSource(new ObjectBasedValueSource(model));
199             try {
200                 value = interpolator.interpolate(value, new PrefixAwareRecursionInterceptor(pomPrefixes));
201             } catch (InterpolationException e) {
202                 throw new ReleaseExecutionException(
203                         "Failed to interpolate " + value + " for project " + model.getId(), e);
204             }
205         }
206         return value;
207     }
208 
209     /**
210      * Check if none of the parent projects have original SCM info in the release descriptor.
211      */
212     public static boolean hasNoOriginalScmInfoInParents(MavenProject project, ReleaseDescriptor releaseDescriptor) {
213         MavenProject parent = project.getParent();
214         while (parent != null) {
215             String parentId = ArtifactUtils.versionlessKey(parent.getGroupId(), parent.getArtifactId());
216             if (releaseDescriptor.hasOriginalScmInfo(parentId)) {
217                 return false;
218             }
219             parent = parent.getParent();
220         }
221 
222         return true;
223     }
224 }