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.transform;
20  
21  import java.nio.file.Files;
22  import java.nio.file.Path;
23  import java.nio.file.Paths;
24  import java.util.List;
25  import java.util.Objects;
26  import java.util.Optional;
27  import java.util.function.Function;
28  import java.util.regex.Pattern;
29  
30  import org.apache.maven.model.transform.pull.NodeBufferingParser;
31  import org.codehaus.plexus.util.xml.pull.XmlPullParser;
32  
33  /**
34   * <p>
35   * Transforms relativePath to version.
36   * We could decide to simply allow {@code <parent/>}, but let's require the GA for now for checking
37   * This filter does NOT remove the relativePath (which is done by {@link RelativePathXMLFilter}, it will only
38   * optionally include the version based on the path
39   * </p>
40   *
41   * @author Robert Scholte
42   * @author Guillaume Nodet
43   * @since 4.0.0
44   */
45  class ParentXMLFilter extends NodeBufferingParser {
46  
47      private final Function<Path, Optional<RelativeProject>> relativePathMapper;
48  
49      private final Path projectPath;
50  
51      private static final Pattern S_FILTER = Pattern.compile("\\s+");
52  
53      /**
54       * @param relativePathMapper
55       */
56      ParentXMLFilter(
57              XmlPullParser parser, Function<Path, Optional<RelativeProject>> relativePathMapper, Path projectPath) {
58          super(parser, "parent");
59          this.relativePathMapper = relativePathMapper;
60          this.projectPath = projectPath;
61      }
62  
63      protected void process(List<Event> buffer) {
64          String tagName = null;
65          String groupId = null;
66          String artifactId = null;
67          String version = null;
68          String relativePath = null;
69          String whitespaceAfterParentStart = "";
70          boolean hasVersion = false;
71          boolean hasRelativePath = false;
72          for (int i = 0; i < buffer.size(); i++) {
73              Event event = buffer.get(i);
74              if (event.event == START_TAG) {
75                  tagName = event.name;
76                  hasVersion |= "version".equals(tagName);
77                  hasRelativePath |= "relativePath".equals(tagName);
78              } else if (event.event == TEXT) {
79                  if (S_FILTER.matcher(event.text).matches()) {
80                      if (whitespaceAfterParentStart.isEmpty()) {
81                          whitespaceAfterParentStart = event.text;
82                      }
83                  } else if ("groupId".equals(tagName)) {
84                      groupId = nullSafeAppend(groupId, event.text);
85                  } else if ("artifactId".equals(tagName)) {
86                      artifactId = nullSafeAppend(artifactId, event.text);
87                  } else if ("relativePath".equals(tagName)) {
88                      relativePath = nullSafeAppend(relativePath, event.text);
89                  } else if ("version".equals(tagName)) {
90                      version = nullSafeAppend(version, event.text);
91                  }
92              } else if (event.event == END_TAG && "parent".equals(event.name)) {
93                  Optional<RelativeProject> resolvedParent;
94                  if (!hasVersion && (!hasRelativePath || relativePath != null)) {
95                      Path relPath = Paths.get(Objects.toString(relativePath, "../pom.xml"));
96                      resolvedParent = resolveRelativePath(relPath, groupId, artifactId);
97                  } else {
98                      resolvedParent = Optional.empty();
99                  }
100                 if (!hasVersion && resolvedParent.isPresent()) {
101                     int pos = buffer.get(i - 1).event == TEXT ? i - 1 : i;
102                     Event e = new Event();
103                     e.event = TEXT;
104                     e.text = whitespaceAfterParentStart;
105                     buffer.add(pos++, e);
106                     e = new Event();
107                     e.event = START_TAG;
108                     e.namespace = buffer.get(0).namespace;
109                     e.prefix = buffer.get(0).prefix;
110                     e.name = "version";
111                     buffer.add(pos++, e);
112                     e = new Event();
113                     e.event = TEXT;
114                     e.text = resolvedParent.get().getVersion();
115                     buffer.add(pos++, e);
116                     e = new Event();
117                     e.event = END_TAG;
118                     e.name = "version";
119                     e.namespace = buffer.get(0).namespace;
120                     e.prefix = buffer.get(0).prefix;
121                     buffer.add(pos++, e);
122                 }
123                 break;
124             }
125         }
126         buffer.forEach(this::pushEvent);
127     }
128 
129     protected Optional<RelativeProject> resolveRelativePath(Path relativePath, String groupId, String artifactId) {
130         Path pomPath = projectPath.resolve(relativePath);
131         if (Files.isDirectory(pomPath)) {
132             pomPath = pomPath.resolve("pom.xml");
133         }
134 
135         Optional<RelativeProject> mappedProject = relativePathMapper.apply(pomPath.normalize());
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 }