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

com.metaeffekt.artifact.analysis.version.token.VersionToken Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2021-2024 the original author or authors.
 *
 * Licensed 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 com.metaeffekt.artifact.analysis.version.token;

import com.metaeffekt.artifact.analysis.utils.StringUtils;
import com.metaeffekt.artifact.analysis.version.VersionModifier;
import com.metaeffekt.artifact.analysis.vulnerability.enrichment.VersionComparator;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class VersionToken {

    private final String value;
    private final VersionTokenType type;
    private List subTokens = null;

    public VersionToken(String value, VersionTokenType type) {
        this(value, type, null);
    }

    public VersionToken(String value, VersionTokenType type, VersionToken subToken) {
        if (value == null) {
            throw new IllegalArgumentException("Value must not be null.");
        } else if (type == null) {
            throw new IllegalArgumentException("Type must not be null.");
        }
        this.value = value;
        this.type = type;
        this.addSubToken(subToken);
    }

    public String getValue() {
        return value;
    }

    public VersionTokenType getType() {
        return type;
    }

    public List getSubTokens() {
        return subTokens;
    }

    public int getSubTokenCount() {
        return subTokens != null ? subTokens.size() : 0;
    }

    public void addSubToken(VersionToken subToken) {
        if (subToken == null) {
            return;
        } else if (subTokens == null) {
            subTokens = new ArrayList<>();
        }
        subTokens.add(subToken);
    }

    public boolean isComparableByString() {
        return isDateNumberOrSemver() || isLetteredSubversion();
    }

    public boolean isDateNumberOrSemver() {
        return type == VersionTokenType.NUMBER_OR_SEMVER || type == VersionTokenType.DATE;
    }

    public boolean isLetteredSubversion() {
        return type == VersionTokenType.STRING && (value.length() == 1 || value.length() == 2 && value.startsWith("z"));
    }

    public boolean isLettersOnly() {
        for (int i = 0; i < value.length(); i++) {
            if (!Character.isLetter(value.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    public VersionToken deriveToken(String newValue) {
        return new VersionToken(newValue, type);
    }

    @Override
    public String toString() {
        return value + (subTokens != null && !subTokens.isEmpty() ? " " + subTokens.stream().map(VersionToken::toString).collect(Collectors.joining(" ")) : "");
    }

    public static boolean isEmpty(VersionToken token) {
        return token == null || StringUtils.isEmpty(token.getValue());
    }

    public final static Comparator VERSION_MODIFIER_COMPARATOR = new VersionModifierComparator(false);
    public final static Comparator VERSION_MODIFIER_COMPARATOR_COMMON_PARTS = new VersionModifierComparator(true);

    private static class VersionModifierComparator implements Comparator {
        private final boolean useOnlyCommonParts;

        private VersionModifierComparator(boolean useOnlyCommonParts) {
            this.useOnlyCommonParts = useOnlyCommonParts;
        }

        public int compare(VersionToken o1, VersionToken o2) {
            if (o1 == null && o2 == null) {
                return 0;
            } else if (o1 == null) {
                return -1;
            } else if (o2 == null) {
                return 1;
            } else {
                final VersionModifier m1 = VersionModifier.fromStringName(o1.getValue());
                final VersionModifier m2 = VersionModifier.fromStringName(o2.getValue());

                final int m1Value = m1 != null ? m1.getValue() : 0;
                final int m2Value = m2 != null ? m2.getValue() : 0;

                final int modifierComparison = Integer.compare(m1Value, m2Value);

                if (modifierComparison != 0) {
                    return modifierComparison;

                } else {
                    final List subTokens1 = o1.getSubTokens();
                    final List subTokens2 = o2.getSubTokens();

                    final boolean isNullOrEmpty1 = subTokens1 == null || subTokens1.isEmpty();
                    final boolean isNullOrEmpty2 = subTokens2 == null || subTokens2.isEmpty();

                    if (useOnlyCommonParts && (isNullOrEmpty1 || isNullOrEmpty2)) {
                        return 0;
                    }

                    if (isNullOrEmpty1 && isNullOrEmpty2) {
                        return 0;
                    } else if (isNullOrEmpty1) {
                        return -1;
                    } else if (isNullOrEmpty2) {
                        return 1;
                    }

                    final int limit = useOnlyCommonParts ? Math.min(subTokens1.size(), subTokens2.size()) : Math.max(subTokens1.size(), subTokens2.size());

                    for (int i = 0; i < limit; i++) {
                        if (i >= subTokens1.size()) {
                            return -1;
                        } else if (i >= subTokens2.size()) {
                            return 1;
                        }

                        final String value1 = subTokens1.get(i).getValue();
                        final String value2 = subTokens2.get(i).getValue();
                        final boolean isLettersOnly1 = subTokens1.get(i).isLettersOnly();
                        final boolean isLettersOnly2 = subTokens2.get(i).isLettersOnly();

                        final int subTokenComparison;
                        if (!isLettersOnly1 && !isLettersOnly2) {
                            subTokenComparison = (useOnlyCommonParts ? VersionComparator.INSTANCE_COMMON_PARTS : VersionComparator.INSTANCE).compare(value1, value2);
                        } else {
                            subTokenComparison = value1.compareTo(value2);
                        }

                        if (subTokenComparison != 0) {
                            return subTokenComparison;
                        }
                    }
                }
            }

            return 0;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy