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