All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.intershop.version.semantic.SemanticVersionResolverImpl Maven / Gradle / Ivy

The newest version!
package com.intershop.version.semantic;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class SemanticVersionResolverImpl implements SemanticVersionResolver
{
    private static final Pattern DECIMAL_NONDECIMAL_SPLIT = Pattern.compile("^(\\d+)(\\D+)");
    private static final Pattern NONDECIMAL_DECIMAL_SPLIT = Pattern.compile("^(\\D+)(\\d+)");
    private static final Map MAP_EXTENSION_TO_TYPE = new HashMap<>();
    static
    {
        MAP_EXTENSION_TO_TYPE.put("local", ExtensionType.DEV);
        MAP_EXTENSION_TO_TYPE.put("snapshot", ExtensionType.DEV);
        MAP_EXTENSION_TO_TYPE.put("a", ExtensionType.DEV);
        MAP_EXTENSION_TO_TYPE.put("alpha", ExtensionType.DEV);
        MAP_EXTENSION_TO_TYPE.put("b", ExtensionType.DEV);
        MAP_EXTENSION_TO_TYPE.put("beta", ExtensionType.DEV);
        MAP_EXTENSION_TO_TYPE.put("dev", ExtensionType.DEV);
        MAP_EXTENSION_TO_TYPE.put("m", ExtensionType.DEV);
        MAP_EXTENSION_TO_TYPE.put("milestone", ExtensionType.DEV);
        MAP_EXTENSION_TO_TYPE.put("rc", ExtensionType.PRE);
        MAP_EXTENSION_TO_TYPE.put("cr", ExtensionType.PRE);
        MAP_EXTENSION_TO_TYPE.put("ea", ExtensionType.PRE);
        MAP_EXTENSION_TO_TYPE.put("preview", ExtensionType.PRE);
        MAP_EXTENSION_TO_TYPE.put("release", ExtensionType.GA);
        MAP_EXTENSION_TO_TYPE.put("+", ExtensionType.BUILD);
        MAP_EXTENSION_TO_TYPE.put("v", ExtensionType.BUILD);
        MAP_EXTENSION_TO_TYPE.put("final", ExtensionType.GA);
        MAP_EXTENSION_TO_TYPE.put("ga", ExtensionType.GA);
        MAP_EXTENSION_TO_TYPE.put("jre", ExtensionType.PLATFORM);
        MAP_EXTENSION_TO_TYPE.put("sp", ExtensionType.POST);
    }
    private static final Map MAP_EXTENSION_TO_ITEM_EXTENSION = new HashMap<>();
    // artificial number to identify build numbers or dates as increment, which are not incrementable
    private static final Integer MAX_SEMANTIC_NUMBER = 10_000;
    static
    {
        // ga alias
        MAP_EXTENSION_TO_ITEM_EXTENSION.put("release", "");
        MAP_EXTENSION_TO_ITEM_EXTENSION.put("final", "");
        MAP_EXTENSION_TO_ITEM_EXTENSION.put("ga", "");
        // build or version extensions
        MAP_EXTENSION_TO_ITEM_EXTENSION.put("v", "");
        MAP_EXTENSION_TO_ITEM_EXTENSION.put("+", "");
        // dev release alias
        MAP_EXTENSION_TO_ITEM_EXTENSION.put("a", "alpha");
        MAP_EXTENSION_TO_ITEM_EXTENSION.put("b", "beta");
        MAP_EXTENSION_TO_ITEM_EXTENSION.put("m", "milestone");
        // pre release alias
        MAP_EXTENSION_TO_ITEM_EXTENSION.put("cr", "rc");
        MAP_EXTENSION_TO_ITEM_EXTENSION.put("preview", "rc");
        MAP_EXTENSION_TO_ITEM_EXTENSION.put("ea", "rc");
    }

    @Override
    public SemanticVersion apply(String version)
    {
        int firstDash = version.indexOf("-");
        List parts = Collections.emptyList();
        List extensions = Collections.emptyList();
        if (firstDash > 0)
        {
            parts = splitVersion(version.substring(0, firstDash));
            extensions = splitVersion(version.substring(firstDash + 1));
        }
        else
        {
            parts = splitVersion(version);
        }
        VersionItem items = convertToItem(parts, extensions);
        return buildVersion(version, items);
    }

    List splitVersion(String version)
    {
        String[] parts = version.split("[.-]");
        List result = new ArrayList<>();
        for (int i = 0; i < parts.length; i++)
        {
            String partOfVersion = parts[i].toLowerCase(Locale.US);
            Optional optNumber = getVersionNumberSegment(partOfVersion);
            if (optNumber.isPresent())
            {
                result.add(partOfVersion);
                continue;
            }
            if (NONDECIMAL_DECIMAL_SPLIT.matcher(partOfVersion).find()
                            || DECIMAL_NONDECIMAL_SPLIT.matcher(partOfVersion).find())
            {
                result.addAll(splitWordNumber(partOfVersion));
                continue;
            }
            result.add(partOfVersion);
        }
        return result;
    }

    private List splitWordNumber(String part)
    {
        List result = new ArrayList<>();
        String openPart = part;
        while(!openPart.isEmpty())
        {
            Matcher matcher = NONDECIMAL_DECIMAL_SPLIT.matcher(openPart);
            if (matcher.find())
            {
                result.add(matcher.group(1));
                result.add(matcher.group(2));
                openPart = openPart.substring(matcher.group(0).length());
                continue;
            }
            matcher = DECIMAL_NONDECIMAL_SPLIT.matcher(openPart);
            if (matcher.find())
            {
                result.add(matcher.group(1));
                result.add(matcher.group(2));
                openPart = openPart.substring(matcher.group(0).length());
                continue;
            }
            result.add(openPart);
            openPart = "";
        }
        return result;
    }

    VersionItem convertToItem(List parts, List extensions)
    {
        VersionItem result = VersionItem.emptyVersion();
        List preDashExtensions = new ArrayList<>();
        for (String currentPosition : parts)
        {
            // is that an integer for a version number
            Optional optNumber = getVersionNumberSegment(currentPosition);

            // was an extension found before
            if (result.getExtension().isEmpty())
            {
                // is that a integer for a version number
                if (optNumber.isPresent())
                {
                    result = result.addNumber(optNumber.get());
                }
                else
                {
                    if (isNumber(currentPosition))
                    {
                        try
                        {
                            result = result.addExtensionNumber(currentPosition);
                        }
                        catch(NumberFormatException e)
                        {
                            result = result.addExtension(getReleaseType(currentPosition),
                                            getReleaseExtension(currentPosition));
                        }
                    }
                    else
                    {
                        result = result.addExtension(getReleaseType(currentPosition),
                                        getReleaseExtension(currentPosition));
                    }
                }
            }
            else
            {
                preDashExtensions.add(currentPosition);
            }
        }
        preDashExtensions.addAll(extensions);
        for (String currentPosition : preDashExtensions)
        {
            // is that an integer for a version number
            if (isNumber(currentPosition))
            {
                // extension exists - so at to extension 1.1 vs 1-1
                result = result.addExtensionNumber(currentPosition);
                // type could be NEUTRAL or GA but with a number extension this needs to be changed
                if (result.getReleaseType().equals(ExtensionType.NEUTRAL)
                                || result.getReleaseType().equals(ExtensionType.GA))
                {
                    // if extension is 0 it's not relevant so at 1 == 1-0
                    result = result.setReleaseType(isNull(currentPosition) ? ExtensionType.GA : ExtensionType.UNSPECIFIED);
                }
            }
            else
            {
                result = result.addExtension(getReleaseType(currentPosition), getReleaseExtension(currentPosition));
            }
        }
        return result;
    }

    private boolean isNull(String releaseExtension)
    {
        return releaseExtension.matches("^0+$");
    }

    private boolean isNumber(String releaseExtension)
    {
        return releaseExtension.matches("^\\d+$");
    }

    private SemanticVersion buildVersion(String version, VersionItem item)
    {
        return new SemanticVersionImpl(version, item);
    }

    private static ExtensionType getReleaseType(String lowerCased)
    {
        return MAP_EXTENSION_TO_TYPE.computeIfAbsent(lowerCased, (a) -> ExtensionType.UNSPECIFIED);
    }

    private static String getReleaseExtension(String lowerCased)
    {
        return MAP_EXTENSION_TO_ITEM_EXTENSION.computeIfAbsent(lowerCased, (a) -> a);
    }

    /**
     * @param part of version (e.g. "1.0.0.v20100202" will split to {"1","0","0","v","20100202")
     * @return a number in case the number exists and is lower than 19700101 assuming that is a data)
     */
    private static Optional getVersionNumberSegment(String part)
    {
        try
        {
            Integer value = part.matches("^\\d+$") ? Integer.parseInt(part) : null;
            if (value != null && value < MAX_SEMANTIC_NUMBER)
            {
                return Optional.of(value);
            }
        }
        catch(NumberFormatException e)
        {
            // decimal but too large
        }
        return Optional.empty();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy