ModelVersionUtils.java

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.maven.cling.invoker.mvnup.goals;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Namespace;

import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.ModelVersions.MODEL_VERSION_4_0_0;
import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.ModelVersions.MODEL_VERSION_4_1_0;
import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.ModelVersions.MODEL_VERSION_4_2_0;
import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.Namespaces.MAVEN_4_0_0_NAMESPACE;
import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.Namespaces.MAVEN_4_1_0_NAMESPACE;
import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.Namespaces.MAVEN_4_2_0_NAMESPACE;
import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.SchemaLocations.MAVEN_4_1_0_SCHEMA_LOCATION;
import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.SchemaLocations.MAVEN_4_2_0_SCHEMA_LOCATION;
import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.XmlElements.MODEL_VERSION;

/**
 * Utility class for handling Maven model version operations during upgrades.
 */
public final class ModelVersionUtils {

    private ModelVersionUtils() {
        // Utility class
    }

    /**
     * Detects the model version from a POM document.
     * Uses both the modelVersion element and namespace URI for detection.
     *
     * @param pomDocument the POM document
     * @return the detected model version
     */
    public static String detectModelVersion(Document pomDocument) {
        Element root = pomDocument.getRootElement();
        Namespace namespace = root.getNamespace();

        // First try to get from modelVersion element
        Element modelVersionElement = root.getChild(MODEL_VERSION, namespace);
        if (modelVersionElement != null) {
            String modelVersion = modelVersionElement.getTextTrim();
            if (!modelVersion.isEmpty()) {
                return modelVersion;
            }
        }

        // Fallback to namespace URI detection
        String namespaceUri = namespace.getURI();
        if (MAVEN_4_2_0_NAMESPACE.equals(namespaceUri)) {
            return MODEL_VERSION_4_2_0;
        } else if (MAVEN_4_1_0_NAMESPACE.equals(namespaceUri)) {
            return MODEL_VERSION_4_1_0;
        } else if (MAVEN_4_0_0_NAMESPACE.equals(namespaceUri)) {
            return MODEL_VERSION_4_0_0;
        }

        // Default fallback
        return MODEL_VERSION_4_0_0;
    }

    /**
     * Checks if a model version is valid for upgrade operations.
     * Currently supports 4.0.0, 4.1.0, and 4.2.0.
     *
     * @param modelVersion the model version to validate
     * @return true if the model version is valid
     */
    public static boolean isValidModelVersion(String modelVersion) {
        return MODEL_VERSION_4_0_0.equals(modelVersion)
                || MODEL_VERSION_4_1_0.equals(modelVersion)
                || MODEL_VERSION_4_2_0.equals(modelVersion);
    }

    /**
     * Checks if an upgrade from one version to another is possible.
     *
     * @param fromVersion the source version
     * @param toVersion the target version
     * @return true if the upgrade is possible
     */
    public static boolean canUpgrade(String fromVersion, String toVersion) {
        if (fromVersion == null || toVersion == null) {
            return false;
        }

        // Support upgrades: 4.0.0 ��� 4.1.0, 4.0.0 ��� 4.2.0, 4.1.0 ��� 4.2.0
        if (MODEL_VERSION_4_0_0.equals(fromVersion)) {
            return MODEL_VERSION_4_1_0.equals(toVersion) || MODEL_VERSION_4_2_0.equals(toVersion);
        }
        if (MODEL_VERSION_4_1_0.equals(fromVersion)) {
            return MODEL_VERSION_4_2_0.equals(toVersion);
        }

        return false;
    }

    /**
     * Checks if a model version is eligible for inference optimizations.
     * Models 4.0.0+ are eligible (4.0.0 has limited inference, 4.1.0+ has full inference).
     *
     * @param modelVersion the model version to check
     * @return true if eligible for inference
     */
    public static boolean isEligibleForInference(String modelVersion) {
        return MODEL_VERSION_4_0_0.equals(modelVersion)
                || MODEL_VERSION_4_1_0.equals(modelVersion)
                || MODEL_VERSION_4_2_0.equals(modelVersion);
    }

    /**
     * Checks if a model version is newer than 4.1.0.
     *
     * @param modelVersion the model version to check
     * @return true if newer than 4.1.0
     */
    public static boolean isNewerThan410(String modelVersion) {
        if (modelVersion == null) {
            return false;
        }

        // Simple version comparison for now
        // This could be enhanced with proper version parsing if needed
        try {
            String[] parts = modelVersion.split("\\.");
            if (parts.length >= 2) {
                int major = Integer.parseInt(parts[0]);
                int minor = Integer.parseInt(parts[1]);

                if (major > 4) {
                    return true;
                }
                if (major == 4 && minor > 1) {
                    return true;
                }
                if (major == 4 && minor == 1 && parts.length > 2) {
                    int patch = Integer.parseInt(parts[2]);
                    return patch > 0;
                }
            }
        } catch (NumberFormatException e) {
            // If we can't parse it, assume it's not newer
            return false;
        }

        return false;
    }

    /**
     * Checks if a model version is greater than or equal to a target version.
     *
     * @param modelVersion the model version to check
     * @param targetVersion the target version to compare against
     * @return true if modelVersion >= targetVersion
     */
    public static boolean isVersionGreaterOrEqual(String modelVersion, String targetVersion) {
        if (modelVersion == null || targetVersion == null) {
            return false;
        }

        // Handle exact equality first
        if (modelVersion.equals(targetVersion)) {
            return true;
        }

        // For now, handle the specific cases we need
        if (MODEL_VERSION_4_1_0.equals(targetVersion)) {
            return MODEL_VERSION_4_1_0.equals(modelVersion) || isNewerThan410(modelVersion);
        }

        // Default to false for unknown comparisons
        return false;
    }

    /**
     * Updates the model version element in a POM document.
     *
     * @param pomDocument the POM document
     * @param newVersion the new model version
     */
    public static void updateModelVersion(Document pomDocument, String newVersion) {
        Element root = pomDocument.getRootElement();
        Namespace namespace = root.getNamespace();

        Element modelVersionElement = root.getChild(MODEL_VERSION, namespace);
        if (modelVersionElement != null) {
            modelVersionElement.setText(newVersion);
        } else {
            // Create new modelVersion element if it doesn't exist
            Element newModelVersionElement = new Element(MODEL_VERSION, namespace);
            newModelVersionElement.setText(newVersion);

            // Insert at the beginning of the document
            root.addContent(0, newModelVersionElement);
        }
    }

    /**
     * Removes the model version element from a POM document.
     * This is used during inference when the model version can be inferred.
     *
     * @param pomDocument the POM document
     * @return true if the element was removed, false if it didn't exist
     */
    public static boolean removeModelVersion(Document pomDocument) {
        Element root = pomDocument.getRootElement();
        Namespace namespace = root.getNamespace();

        Element modelVersionElement = root.getChild(MODEL_VERSION, namespace);
        if (modelVersionElement != null) {
            return root.removeContent(modelVersionElement);
        }
        return false;
    }

    /**
     * Gets the schema location for a model version.
     *
     * @param modelVersion the model version
     * @return the schema location
     */
    public static String getSchemaLocationForModelVersion(String modelVersion) {
        if (MODEL_VERSION_4_2_0.equals(modelVersion)) {
            return MAVEN_4_2_0_SCHEMA_LOCATION;
        } else if (MODEL_VERSION_4_1_0.equals(modelVersion)) {
            return MAVEN_4_1_0_SCHEMA_LOCATION;
        } else if (isNewerThan410(modelVersion)) {
            // For versions newer than 4.1.0 but not specifically 4.2.0, use 4.2.0 schema
            return MAVEN_4_2_0_SCHEMA_LOCATION;
        }
        return UpgradeConstants.SchemaLocations.MAVEN_4_0_0_SCHEMA_LOCATION;
    }
}