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.cling.invoker.mvnup.goals;
20  
21  import eu.maveniverse.domtrip.Document;
22  import eu.maveniverse.domtrip.Editor;
23  import eu.maveniverse.domtrip.Element;
24  
25  import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.MODEL_VERSION;
26  import static eu.maveniverse.domtrip.maven.MavenPomElements.ModelVersions.MODEL_VERSION_4_0_0;
27  import static eu.maveniverse.domtrip.maven.MavenPomElements.ModelVersions.MODEL_VERSION_4_1_0;
28  import static eu.maveniverse.domtrip.maven.MavenPomElements.ModelVersions.MODEL_VERSION_4_2_0;
29  import static eu.maveniverse.domtrip.maven.MavenPomElements.Namespaces.MAVEN_4_0_0_NAMESPACE;
30  import static eu.maveniverse.domtrip.maven.MavenPomElements.Namespaces.MAVEN_4_1_0_NAMESPACE;
31  import static eu.maveniverse.domtrip.maven.MavenPomElements.Namespaces.MAVEN_4_2_0_NAMESPACE;
32  import static eu.maveniverse.domtrip.maven.MavenPomElements.SchemaLocations.MAVEN_4_0_0_SCHEMA_LOCATION;
33  import static eu.maveniverse.domtrip.maven.MavenPomElements.SchemaLocations.MAVEN_4_1_0_SCHEMA_LOCATION;
34  import static eu.maveniverse.domtrip.maven.MavenPomElements.SchemaLocations.MAVEN_4_2_0_SCHEMA_LOCATION;
35  
36  /**
37   * Utility class for handling Maven model version operations during upgrades.
38   *
39   * <p>This class uses domtrip internally for superior formatting preservation
40   * and simplified API while maintaining the same external interface.
41   */
42  public final class ModelVersionUtils {
43  
44      private ModelVersionUtils() {
45          // Utility class
46      }
47  
48      /**
49       * Detects the model version from a POM document.
50       * Uses both the modelVersion element and namespace URI for detection.
51       *
52       * @param pomDocument the POM document (domtrip Document)
53       * @return the detected model version
54       */
55      public static String detectModelVersion(Document pomDocument) {
56          Editor editor = new Editor(pomDocument);
57          Element root = editor.root();
58          if (root == null) {
59              return MODEL_VERSION_4_0_0;
60          }
61  
62          // First try to get from modelVersion element
63          Element modelVersionElement = root.child(MODEL_VERSION).orElse(null);
64          if (modelVersionElement != null) {
65              String modelVersion = modelVersionElement.textContentTrimmed();
66              if (!modelVersion.isEmpty()) {
67                  return modelVersion;
68              }
69          }
70  
71          // Fallback to namespace URI detection
72          String namespaceUri = root.namespaceDeclaration(null);
73          if (MAVEN_4_2_0_NAMESPACE.equals(namespaceUri)) {
74              return MODEL_VERSION_4_2_0;
75          } else if (MAVEN_4_1_0_NAMESPACE.equals(namespaceUri)) {
76              return MODEL_VERSION_4_1_0;
77          } else if (MAVEN_4_0_0_NAMESPACE.equals(namespaceUri)) {
78              return MODEL_VERSION_4_0_0;
79          }
80  
81          // Default fallback
82          return MODEL_VERSION_4_0_0;
83      }
84  
85      /**
86       * Checks if a model version is valid for upgrade operations.
87       * Currently supports 4.0.0, 4.1.0, and 4.2.0.
88       *
89       * @param modelVersion the model version to validate
90       * @return true if the model version is valid
91       */
92      public static boolean isValidModelVersion(String modelVersion) {
93          return MODEL_VERSION_4_0_0.equals(modelVersion)
94                  || MODEL_VERSION_4_1_0.equals(modelVersion)
95                  || MODEL_VERSION_4_2_0.equals(modelVersion);
96      }
97  
98      /**
99       * Checks if an upgrade from one version to another is possible.
100      *
101      * @param fromVersion the source version
102      * @param toVersion the target version
103      * @return true if the upgrade is possible
104      */
105     public static boolean canUpgrade(String fromVersion, String toVersion) {
106         if (fromVersion == null || toVersion == null) {
107             return false;
108         }
109 
110         // Support upgrades: 4.0.0 → 4.1.0, 4.0.0 → 4.2.0, 4.1.0 → 4.2.0
111         if (MODEL_VERSION_4_0_0.equals(fromVersion)) {
112             return MODEL_VERSION_4_1_0.equals(toVersion) || MODEL_VERSION_4_2_0.equals(toVersion);
113         }
114         if (MODEL_VERSION_4_1_0.equals(fromVersion)) {
115             return MODEL_VERSION_4_2_0.equals(toVersion);
116         }
117 
118         return false;
119     }
120 
121     /**
122      * Checks if a model version is eligible for inference optimizations.
123      * Models 4.0.0+ are eligible (4.0.0 has limited inference, 4.1.0+ has full inference).
124      *
125      * @param modelVersion the model version to check
126      * @return true if eligible for inference
127      */
128     public static boolean isEligibleForInference(String modelVersion) {
129         return MODEL_VERSION_4_0_0.equals(modelVersion)
130                 || MODEL_VERSION_4_1_0.equals(modelVersion)
131                 || MODEL_VERSION_4_2_0.equals(modelVersion);
132     }
133 
134     /**
135      * Checks if a model version is newer than 4.1.0.
136      *
137      * @param modelVersion the model version to check
138      * @return true if newer than 4.1.0
139      */
140     public static boolean isNewerThan410(String modelVersion) {
141         if (modelVersion == null) {
142             return false;
143         }
144 
145         // Simple version comparison for now
146         // This could be enhanced with proper version parsing if needed
147         try {
148             String[] parts = modelVersion.split("\\.");
149             if (parts.length >= 2) {
150                 int major = Integer.parseInt(parts[0]);
151                 int minor = Integer.parseInt(parts[1]);
152 
153                 if (major > 4) {
154                     return true;
155                 }
156                 if (major == 4 && minor > 1) {
157                     return true;
158                 }
159                 if (major == 4 && minor == 1 && parts.length > 2) {
160                     int patch = Integer.parseInt(parts[2]);
161                     return patch > 0;
162                 }
163             }
164         } catch (NumberFormatException e) {
165             // If we can't parse it, assume it's not newer
166             return false;
167         }
168 
169         return false;
170     }
171 
172     /**
173      * Checks if a model version is greater than or equal to a target version.
174      *
175      * @param modelVersion the model version to check
176      * @param targetVersion the target version to compare against
177      * @return true if modelVersion >= targetVersion
178      */
179     public static boolean isVersionGreaterOrEqual(String modelVersion, String targetVersion) {
180         if (modelVersion == null || targetVersion == null) {
181             return false;
182         }
183 
184         // Handle exact equality first
185         if (modelVersion.equals(targetVersion)) {
186             return true;
187         }
188 
189         // For now, handle the specific cases we need
190         if (MODEL_VERSION_4_1_0.equals(targetVersion)) {
191             return isNewerThan410(modelVersion);
192         }
193 
194         // Default to false for unknown comparisons
195         return false;
196     }
197 
198     /**
199      * Updates the model version element in a POM document.
200      *
201      * @param pomDocument the POM document (domtrip Document)
202      * @param newVersion the new model version
203      */
204     public static void updateModelVersion(Document pomDocument, String newVersion) {
205         Editor editor = new Editor(pomDocument);
206         Element root = editor.root();
207         if (root == null) {
208             return;
209         }
210 
211         Element modelVersionElement = root.child(MODEL_VERSION).orElse(null);
212         if (modelVersionElement != null) {
213             editor.setTextContent(modelVersionElement, newVersion);
214         } else {
215             // Create new modelVersion element if it doesn't exist
216             // domtrip will automatically handle proper positioning and formatting
217             DomUtils.insertContentElement(root, MODEL_VERSION, newVersion);
218         }
219     }
220 
221     /**
222      * Removes the model version element from a POM editor.
223      * This is used during inference when the model version can be inferred.
224      *
225      * @param document the XML document
226      * @return true if the element was removed, false if it didn't exist
227      */
228     public static boolean removeModelVersion(Document document) {
229         Element root = document.root();
230         if (root == null) {
231             return false;
232         }
233 
234         Element modelVersionElement = root.child(MODEL_VERSION).orElse(null);
235         if (modelVersionElement != null) {
236             return root.removeNode(modelVersionElement);
237         }
238         return false;
239     }
240 
241     /**
242      * Gets the schema location for a model version.
243      *
244      * @param modelVersion the model version
245      * @return the schema location
246      */
247     public static String getSchemaLocationForModelVersion(String modelVersion) {
248         if (MODEL_VERSION_4_2_0.equals(modelVersion)) {
249             return MAVEN_4_2_0_SCHEMA_LOCATION;
250         } else if (MODEL_VERSION_4_1_0.equals(modelVersion)) {
251             return MAVEN_4_1_0_SCHEMA_LOCATION;
252         } else if (isNewerThan410(modelVersion)) {
253             // For versions newer than 4.1.0 but not specifically 4.2.0, use 4.2.0 schema
254             return MAVEN_4_2_0_SCHEMA_LOCATION;
255         }
256         return MAVEN_4_0_0_SCHEMA_LOCATION;
257     }
258 }