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