Skip to content

Commit a336a2c

Browse files
authored
Switch mvnup to domtrip (#11432)
1 parent 405e2e1 commit a336a2c

34 files changed

+2343
-3146
lines changed

apache-maven/src/main/appended-resources/META-INF/LICENSE.vm

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ subject to the terms and conditions of the following licenses:
3737
#* *##set ( $spdx = 'MIT' )
3838
#* *##elseif ( $license.name == "Eclipse Public License, Version 1.0" )
3939
#* *##set ( $spdx = 'EPL-1.0' )
40-
#* *##elseif ( $license.name == "Eclipse Public License, Version 2.0" )
40+
#* *##elseif ( $license.name == "Eclipse Public License, Version 2.0"
41+
|| $license.url.contains( "https://www.eclipse.org/legal/epl-v20.html" ) )
4142
#* *##set ( $spdx = 'EPL-2.0' )
4243
#* *##elseif ( $license.url.contains( "www.apache.org/licenses/LICENSE-2.0" )
4344
|| $license.url.contains( "https://raw.github.com/hunterhacker/jdom/master/LICENSE.txt" ) )

impl/maven-cli/pom.xml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,13 @@ under the License.
160160
</dependency>
161161

162162
<dependency>
163-
<groupId>org.jdom</groupId>
164-
<artifactId>jdom2</artifactId>
163+
<groupId>eu.maveniverse.maven.domtrip</groupId>
164+
<artifactId>domtrip-core</artifactId>
165+
</dependency>
166+
167+
<dependency>
168+
<groupId>eu.maveniverse.maven.domtrip</groupId>
169+
<artifactId>domtrip-maven</artifactId>
165170
</dependency>
166171

167172
<dependency>

impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/UpgradeContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import org.jline.utils.AttributedStringBuilder;
3131
import org.jline.utils.AttributedStyle;
3232

33-
import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.Indentation;
33+
import static eu.maveniverse.domtrip.maven.MavenPomElements.Indentation;
3434

3535
@SuppressWarnings("VisibilityModifier")
3636
public class UpgradeContext extends LookupContext {

impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/AbstractUpgradeGoal.java

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,22 @@
1818
*/
1919
package org.apache.maven.cling.invoker.mvnup.goals;
2020

21-
import java.io.ByteArrayOutputStream;
2221
import java.io.IOException;
23-
import java.io.OutputStream;
24-
import java.nio.charset.StandardCharsets;
2522
import java.nio.file.Files;
2623
import java.nio.file.Path;
2724
import java.nio.file.Paths;
2825
import java.util.Map;
2926

27+
import eu.maveniverse.domtrip.Document;
28+
import eu.maveniverse.domtrip.DomTripException;
29+
import eu.maveniverse.domtrip.maven.MavenPomElements;
3030
import org.apache.maven.api.cli.mvnup.UpgradeOptions;
3131
import org.apache.maven.api.di.Inject;
3232
import org.apache.maven.cling.invoker.mvnup.Goal;
3333
import org.apache.maven.cling.invoker.mvnup.UpgradeContext;
34-
import org.jdom2.Document;
35-
import org.jdom2.JDOMException;
36-
import org.jdom2.output.Format;
37-
import org.jdom2.output.XMLOutputter;
3834

39-
import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.Files.MVN_DIRECTORY;
40-
import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.ModelVersions.MODEL_VERSION_4_1_0;
35+
import static eu.maveniverse.domtrip.maven.MavenPomElements.Files.MVN_DIRECTORY;
36+
import static eu.maveniverse.domtrip.maven.MavenPomElements.ModelVersions.MODEL_VERSION_4_1_0;
4137

4238
/**
4339
* Base class for upgrade goals containing shared functionality.
@@ -160,7 +156,7 @@ public int execute(UpgradeContext context) throws Exception {
160156
} else if (options.all().orElse(false)) {
161157
targetModel = MODEL_VERSION_4_1_0;
162158
} else {
163-
targetModel = UpgradeConstants.ModelVersions.MODEL_VERSION_4_0_0;
159+
targetModel = MavenPomElements.ModelVersions.MODEL_VERSION_4_0_0;
164160
}
165161

166162
if (!ModelVersionUtils.isValidModelVersion(targetModel)) {
@@ -176,7 +172,7 @@ public int execute(UpgradeContext context) throws Exception {
176172
Map<Path, Document> pomMap;
177173
try {
178174
pomMap = PomDiscovery.discoverPoms(startingDirectory);
179-
} catch (IOException | JDOMException e) {
175+
} catch (IOException | DomTripException e) {
180176
context.failure("Failed to discover POM files: " + e.getMessage());
181177
return 1;
182178
}
@@ -212,7 +208,7 @@ protected int doUpgrade(UpgradeContext context, String targetModel, Map<Path, Do
212208
// This is needed for both 4.0.0 and 4.1.0 to help Maven find the project root
213209
createMvnDirectoryIfNeeded(context);
214210

215-
return result.success() ? 0 : 1;
211+
return result.errorPoms().isEmpty() ? 0 : 1;
216212
} catch (Exception e) {
217213
context.failure("Strategy execution failed: " + e.getMessage());
218214
return 1;
@@ -226,7 +222,7 @@ protected int doUpgrade(UpgradeContext context, String targetModel, Map<Path, Do
226222
protected abstract boolean shouldSaveModifications();
227223

228224
/**
229-
* Saves the modified documents to disk.
225+
* Saves the modified documents to disk using domtrip's perfect formatting preservation.
230226
*/
231227
protected void saveModifications(UpgradeContext context, Map<Path, Document> pomMap) {
232228
context.info("");
@@ -236,27 +232,16 @@ protected void saveModifications(UpgradeContext context, Map<Path, Document> pom
236232
Path pomPath = entry.getKey();
237233
Document document = entry.getValue();
238234
try {
239-
String content = Files.readString(entry.getKey(), StandardCharsets.UTF_8);
240-
int startIndex = content.indexOf("<" + document.getRootElement().getName());
241-
String head = startIndex >= 0 ? content.substring(0, startIndex) : "";
242-
String lastTag = document.getRootElement().getName() + ">";
243-
int endIndex = content.lastIndexOf(lastTag);
244-
String tail = endIndex >= 0 ? content.substring(endIndex + lastTag.length()) : "";
245-
Format format = Format.getRawFormat();
246-
format.setLineSeparator(System.lineSeparator());
247-
XMLOutputter out = new XMLOutputter(format);
248-
ByteArrayOutputStream output = new ByteArrayOutputStream();
249-
try (OutputStream outputStream = output) {
250-
outputStream.write(head.getBytes(StandardCharsets.UTF_8));
251-
out.output(document.getRootElement(), outputStream);
252-
outputStream.write(tail.getBytes(StandardCharsets.UTF_8));
253-
}
254-
String newBody = output.toString(StandardCharsets.UTF_8);
255-
Files.writeString(pomPath, newBody, StandardCharsets.UTF_8);
235+
// Use domtrip for perfect formatting preservation
236+
String xmlContent = DomUtils.toXml(document);
237+
Files.writeString(pomPath, xmlContent);
238+
context.detail("Saved: " + pomPath);
256239
} catch (Exception e) {
257240
context.failure("Failed to save " + pomPath + ": " + e.getMessage());
258241
}
259242
}
243+
244+
context.success("All modifications saved successfully");
260245
}
261246

262247
/**

impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/AbstractUpgradeStrategy.java

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,30 @@
1919
package org.apache.maven.cling.invoker.mvnup.goals;
2020

2121
import java.nio.file.Path;
22+
import java.util.HashSet;
2223
import java.util.Map;
2324
import java.util.Set;
2425

26+
import eu.maveniverse.domtrip.Document;
27+
import eu.maveniverse.domtrip.Element;
28+
import eu.maveniverse.domtrip.maven.Coordinates;
29+
import eu.maveniverse.domtrip.maven.MavenPomElements;
2530
import org.apache.maven.api.cli.mvnup.UpgradeOptions;
2631
import org.apache.maven.cling.invoker.mvnup.UpgradeContext;
27-
import org.jdom2.Document;
32+
33+
import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.PARENT;
2834

2935
/**
3036
* Abstract base class for upgrade strategies that provides common functionality
3137
* and reduces code duplication across strategy implementations.
38+
*
39+
* <p>Strategies work with domtrip Documents for perfect formatting preservation.
40+
* Subclasses can create domtrip Editors from Documents as needed:
41+
* <pre>
42+
* Editor editor = new Editor(document);
43+
* // ... perform domtrip operations ...
44+
* // Document is automatically updated
45+
* </pre>
3246
*/
3347
public abstract class AbstractUpgradeStrategy implements UpgradeStrategy {
3448

@@ -92,4 +106,82 @@ protected void logSummary(UpgradeContext context, UpgradeResult result) {
92106
}
93107
context.unindent();
94108
}
109+
110+
/**
111+
* Extracts an Artifact from a POM document with parent resolution.
112+
* If groupId or version are missing, attempts to resolve from parent.
113+
*
114+
* <p>This method handles Maven's inheritance mechanism where groupId and version
115+
* can be inherited from the parent POM.
116+
*
117+
* @param context the upgrade context for logging
118+
* @param pomDocument the POM document
119+
* @return the Artifact or null if it cannot be determined
120+
*/
121+
public static Coordinates extractArtifactCoordinatesWithParentResolution(
122+
UpgradeContext context, Document pomDocument) {
123+
Element root = pomDocument.root();
124+
125+
// Extract direct values
126+
String groupId = root.childTextTrimmed(MavenPomElements.Elements.GROUP_ID);
127+
String artifactId = root.childTextTrimmed(MavenPomElements.Elements.ARTIFACT_ID);
128+
String version = root.childTextTrimmed(MavenPomElements.Elements.VERSION);
129+
130+
// If groupId or version is missing, try to get from parent
131+
if (groupId == null || version == null) {
132+
Element parentElement = root.child(PARENT).orElse(null);
133+
if (parentElement != null) {
134+
if (groupId == null) {
135+
groupId = parentElement.childTextTrimmed(MavenPomElements.Elements.GROUP_ID);
136+
}
137+
if (version == null) {
138+
version = parentElement.childTextTrimmed(MavenPomElements.Elements.VERSION);
139+
}
140+
}
141+
}
142+
143+
// ArtifactId is required and cannot be inherited
144+
if (artifactId == null || artifactId.isEmpty()) {
145+
context.debug("Cannot determine artifactId for POM");
146+
return null;
147+
}
148+
149+
// GroupId and version can be inherited, but if still null, we can't create a valid Artifact
150+
if (groupId == null || groupId.isEmpty() || version == null || version.isEmpty()) {
151+
context.debug("Cannot determine complete GAV for artifactId: " + artifactId);
152+
return null;
153+
}
154+
155+
return Coordinates.of(groupId, artifactId, version);
156+
}
157+
158+
/**
159+
* Computes all artifacts from all POMs in a multi-module project.
160+
* This includes resolving parent inheritance.
161+
*
162+
* @param context the upgrade context for logging
163+
* @param pomMap map of all POM files in the project
164+
* @return set of all Artifacts in the project
165+
*/
166+
public static Set<Coordinates> computeAllArtifactCoordinates(UpgradeContext context, Map<Path, Document> pomMap) {
167+
Set<Coordinates> coordinates = new HashSet<>();
168+
169+
context.info("Computing artifacts for inference from " + pomMap.size() + " POM(s)...");
170+
171+
// Extract artifact from all POMs in the project
172+
for (Map.Entry<Path, Document> entry : pomMap.entrySet()) {
173+
Path pomPath = entry.getKey();
174+
Document pomDocument = entry.getValue();
175+
176+
Coordinates coordinate =
177+
AbstractUpgradeStrategy.extractArtifactCoordinatesWithParentResolution(context, pomDocument);
178+
if (coordinate != null) {
179+
coordinates.add(coordinate);
180+
context.debug("Found artifact: " + coordinate.toGAV() + " from " + pomPath);
181+
}
182+
}
183+
184+
context.info("Computed " + coordinates.size() + " unique artifact(s) for inference");
185+
return coordinates;
186+
}
95187
}

0 commit comments

Comments
 (0)