1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.cling.invoker.mvnup.goals;
20
21 import java.nio.file.Path;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26
27 import org.apache.maven.api.cli.mvnup.UpgradeOptions;
28 import org.apache.maven.api.di.Named;
29 import org.apache.maven.api.di.Priority;
30 import org.apache.maven.api.di.Singleton;
31 import org.apache.maven.cling.invoker.mvnup.UpgradeContext;
32 import org.jdom2.Attribute;
33 import org.jdom2.Document;
34 import org.jdom2.Element;
35 import org.jdom2.Namespace;
36
37 import static org.apache.maven.cling.invoker.mvnup.goals.ModelVersionUtils.getSchemaLocationForModelVersion;
38 import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.ModelVersions.MODEL_VERSION_4_0_0;
39 import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.ModelVersions.MODEL_VERSION_4_1_0;
40 import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.Namespaces.MAVEN_4_0_0_NAMESPACE;
41 import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.Namespaces.MAVEN_4_1_0_NAMESPACE;
42 import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.XmlAttributes.SCHEMA_LOCATION;
43 import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.XmlAttributes.XSI_NAMESPACE_PREFIX;
44 import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.XmlAttributes.XSI_NAMESPACE_URI;
45 import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.XmlElements.MODULE;
46 import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.XmlElements.MODULES;
47 import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.XmlElements.PROFILE;
48 import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.XmlElements.PROFILES;
49 import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.XmlElements.SUBPROJECT;
50 import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.XmlElements.SUBPROJECTS;
51
52
53
54
55
56 @Named
57 @Singleton
58 @Priority(40)
59 public class ModelUpgradeStrategy extends AbstractUpgradeStrategy {
60
61 public ModelUpgradeStrategy() {
62
63 }
64
65 @Override
66 public boolean isApplicable(UpgradeContext context) {
67 UpgradeOptions options = getOptions(context);
68
69
70 if (options.all().orElse(false)) {
71 return true;
72 }
73
74 String targetModel = determineTargetModelVersion(context);
75
76 return !MODEL_VERSION_4_0_0.equals(targetModel);
77 }
78
79 @Override
80 public String getDescription() {
81 return "Upgrading POM model version";
82 }
83
84 @Override
85 public UpgradeResult doApply(UpgradeContext context, Map<Path, Document> pomMap) {
86 String targetModelVersion = determineTargetModelVersion(context);
87
88 Set<Path> processedPoms = new HashSet<>();
89 Set<Path> modifiedPoms = new HashSet<>();
90 Set<Path> errorPoms = new HashSet<>();
91
92 for (Map.Entry<Path, Document> entry : pomMap.entrySet()) {
93 Path pomPath = entry.getKey();
94 Document pomDocument = entry.getValue();
95 processedPoms.add(pomPath);
96
97 String currentVersion = ModelVersionUtils.detectModelVersion(pomDocument);
98 context.info(pomPath + " (current: " + currentVersion + ")");
99 context.indent();
100
101 try {
102 if (currentVersion.equals(targetModelVersion)) {
103 context.success("Already at target version " + targetModelVersion);
104 } else if (ModelVersionUtils.canUpgrade(currentVersion, targetModelVersion)) {
105 context.action("Upgrading from " + currentVersion + " to " + targetModelVersion);
106
107
108 context.indent();
109 try {
110 performModelUpgrade(pomDocument, context, currentVersion, targetModelVersion);
111 } finally {
112 context.unindent();
113 }
114 context.success("Model upgrade completed");
115 modifiedPoms.add(pomPath);
116 } else {
117 context.warning("Cannot upgrade from " + currentVersion + " to " + targetModelVersion);
118 }
119 } catch (Exception e) {
120 context.failure("Model upgrade failed: " + e.getMessage());
121 errorPoms.add(pomPath);
122 } finally {
123 context.unindent();
124 }
125 }
126
127 return new UpgradeResult(processedPoms, modifiedPoms, errorPoms);
128 }
129
130
131
132
133
134 private void performModelUpgrade(
135 Document pomDocument, UpgradeContext context, String currentVersion, String targetModelVersion) {
136
137 upgradeNamespaceAndSchemaLocation(pomDocument, context, targetModelVersion);
138
139
140 if (ModelVersionUtils.isVersionGreaterOrEqual(targetModelVersion, MODEL_VERSION_4_1_0)) {
141 convertModulesToSubprojects(pomDocument, context);
142 }
143
144
145 ModelVersionUtils.updateModelVersion(pomDocument, targetModelVersion);
146 context.detail("Updated modelVersion to " + targetModelVersion);
147 }
148
149
150
151
152 private void upgradeNamespaceAndSchemaLocation(
153 Document pomDocument, UpgradeContext context, String targetModelVersion) {
154 Element root = pomDocument.getRootElement();
155
156
157 String targetNamespace = getNamespaceForModelVersion(targetModelVersion);
158 Namespace newNamespace = Namespace.getNamespace(targetNamespace);
159 updateElementNamespace(root, newNamespace);
160 context.detail("Updated namespace to " + targetNamespace);
161
162
163 Attribute schemaLocationAttr =
164 root.getAttribute(SCHEMA_LOCATION, Namespace.getNamespace(XSI_NAMESPACE_PREFIX, XSI_NAMESPACE_URI));
165 if (schemaLocationAttr != null) {
166 schemaLocationAttr.setValue(getSchemaLocationForModelVersion(targetModelVersion));
167 context.detail("Updated xsi:schemaLocation");
168 }
169 }
170
171
172
173
174 private void updateElementNamespace(Element element, Namespace newNamespace) {
175 element.setNamespace(newNamespace);
176 for (Element child : element.getChildren()) {
177 updateElementNamespace(child, newNamespace);
178 }
179 }
180
181
182
183
184 private void convertModulesToSubprojects(Document pomDocument, UpgradeContext context) {
185 Element root = pomDocument.getRootElement();
186 Namespace namespace = root.getNamespace();
187
188
189 Element modulesElement = root.getChild(MODULES, namespace);
190 if (modulesElement != null) {
191 modulesElement.setName(SUBPROJECTS);
192 context.detail("Converted <modules> to <subprojects>");
193
194
195 List<Element> moduleElements = modulesElement.getChildren(MODULE, namespace);
196 for (Element moduleElement : moduleElements) {
197 moduleElement.setName(SUBPROJECT);
198 }
199
200 if (!moduleElements.isEmpty()) {
201 context.detail("Converted " + moduleElements.size() + " <module> elements to <subproject>");
202 }
203 }
204
205
206 Element profilesElement = root.getChild(PROFILES, namespace);
207 if (profilesElement != null) {
208 List<Element> profileElements = profilesElement.getChildren(PROFILE, namespace);
209 for (Element profileElement : profileElements) {
210 Element profileModulesElement = profileElement.getChild(MODULES, namespace);
211 if (profileModulesElement != null) {
212 profileModulesElement.setName(SUBPROJECTS);
213
214 List<Element> profileModuleElements = profileModulesElement.getChildren(MODULE, namespace);
215 for (Element moduleElement : profileModuleElements) {
216 moduleElement.setName(SUBPROJECT);
217 }
218
219 if (!profileModuleElements.isEmpty()) {
220 context.detail("Converted " + profileModuleElements.size()
221 + " <module> elements to <subproject> in profiles");
222 }
223 }
224 }
225 }
226 }
227
228
229
230
231 private String determineTargetModelVersion(UpgradeContext context) {
232 UpgradeOptions options = getOptions(context);
233
234 if (options.modelVersion().isPresent()) {
235 return options.modelVersion().get();
236 } else if (options.all().orElse(false)) {
237 return MODEL_VERSION_4_1_0;
238 } else {
239 return MODEL_VERSION_4_0_0;
240 }
241 }
242
243
244
245
246 private String getNamespaceForModelVersion(String modelVersion) {
247 if (MODEL_VERSION_4_1_0.equals(modelVersion)) {
248 return MAVEN_4_1_0_NAMESPACE;
249 } else {
250 return MAVEN_4_0_0_NAMESPACE;
251 }
252 }
253 }