/** * Copyright (c) 2010, 2013 Darmstadt University of Technology. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Andreas Sewe - initial API and implementation. * Olav Lenz - Add methods for version strings. */ package org.eclipse.recommenders.utils; import java.util.Collection; import java.util.LinkedList; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.google.common.base.Function; import com.google.common.collect.Iterables; public final class Versions { private static final Pattern VERSION_PATTERNS = Pattern .compile("(([1-9][0-9]*)|[0-9])(\\.(([1-9][0-9]*)|[0-9])){2}"); private static final Pattern FIND_VERSION_PATTERN = Pattern .compile("(([1-9][0-9]*)|[0-9])(\\.(([1-9][0-9]*)|[0-9])){0,2}"); private Versions() { } public static Version findClosest(final Version startingPoint, Collection<Version> candidates) { Collection<Version> closestCandidates = closestCandidates(candidates, new MajorDistance(startingPoint)); closestCandidates = closestCandidates(closestCandidates, new MinorDistance(startingPoint)); closestCandidates = closestCandidates(closestCandidates, new PatchDistance(startingPoint)); return Iterables.getOnlyElement(closestCandidates); } private static Collection<Version> closestCandidates(Collection<Version> candidates, Function<Version, Integer> metric) { Collection<Version> closestCandidatesByMajorVersion = new LinkedList<>(); int closestDistance = Integer.MAX_VALUE; for (Version candidate : candidates) { int distance = metric.apply(candidate); if (distance < closestDistance) { closestDistance = distance; closestCandidatesByMajorVersion.clear(); closestCandidatesByMajorVersion.add(candidate); } else if (distance == closestDistance) { closestCandidatesByMajorVersion.add(candidate); } } return closestCandidatesByMajorVersion; } private static class MajorDistance implements Function<Version, Integer> { private final Version referencePoint; public MajorDistance(Version referencePoint) { this.referencePoint = referencePoint; } @Override public Integer apply(Version version) { if (version.compareTo(referencePoint) >= 0) { return 2 * (version.getMajor() - referencePoint.getMajor()) + 1; } else { return 2 * (referencePoint.getMajor() - version.getMajor()); } } } private static class MinorDistance implements Function<Version, Integer> { private final Version referencePoint; public MinorDistance(Version referencePoint) { this.referencePoint = referencePoint; } @Override public Integer apply(Version version) { if (version.compareTo(referencePoint) >= 0) { return 2 * (version.getMinor() - referencePoint.getMinor()) + 1; } else { return 2 * (referencePoint.getMinor() - version.getMinor()); } } } private static class PatchDistance implements Function<Version, Integer> { private final Version referencePoint; public PatchDistance(Version referencePoint) { this.referencePoint = referencePoint; } @Override public Integer apply(Version version) { if (version.compareTo(referencePoint) >= 0) { return 2 * (version.getPatch() - referencePoint.getPatch()) + 1; } else { return 2 * (referencePoint.getPatch() - version.getPatch()); } } } /** * Checks if the version has the correct format. * * The version must have the following structure: <code>major.minor.micro</code> where major, minor and micro are * any number (but w\o leading 0). */ public static boolean isValidVersion(String version) { return VERSION_PATTERNS.matcher(version).matches(); } /** * Canonicalize a given version string. If it is possible a version with the following structure will be extracted: * <code>major.minor.micro</code> where major, minor and micro are any number (but w\o leading 0). If a minor and/or * micro version number is missing '.0' will be added for them. * <p> * * If it is not possible to extract the version the input value is returned. */ public static String canonicalizeVersion(String version) { Matcher matcher = FIND_VERSION_PATTERN.matcher(version); if (matcher.find()) { String temp = version.substring(matcher.start(), matcher.end()); return addMissingVersionPartsIfNecessary(temp); } return version; } /** * Add '.0' as minor and micro version if they are missing in the string. The method counts the '.' contained in the * string and add a '.0' if the number of '.' is smaller than 2 or ".0.0" if the number of '.' is 0. */ private static String addMissingVersionPartsIfNecessary(String version) { StringBuilder temp = new StringBuilder(version); String[] parts = version.split("\\."); int missingVersionParts = 3 - parts.length; for (int i = 0; i < missingVersionParts; i++) { temp.append(".0"); } return temp.toString(); } }