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