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

org.springframework.web.accept.SemanticApiVersionParser Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2002-2025 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
 *
 *      https://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.springframework.web.accept;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.springframework.util.Assert;

/**
 * Parser for semantic API versioning with  a major, minor, and patch values.
 * For example "1", "1.0", "1.2", "1.2.0", "1.2.3". Leading, non-integer
 * characters, as in "v1.0", are skipped.
 *
 * @author Rossen Stoyanchev
 * @since 7.0
 */
public class SemanticApiVersionParser implements ApiVersionParser {

	private static final Pattern semantinVersionPattern = Pattern.compile("^(\\d+)(\\.(\\d+))?(\\.(\\d+))?$");


	@Override
	public Version parseVersion(String version) {
		Assert.notNull(version, "'version' is required");

		version = skipNonDigits(version);

		Matcher matcher = semantinVersionPattern.matcher(version);
		Assert.state(matcher.matches(), "Invalid API version format");

		String major = matcher.group(1);
		String minor = matcher.group(3);
		String patch = matcher.group(5);

		return new Version(
				Integer.parseInt(major),
				(minor != null ? Integer.parseInt(minor) : 0),
				(patch != null ? Integer.parseInt(patch) : 0));
	}

	private static String skipNonDigits(String value) {
		for (int i = 0; i < value.length(); i++) {
			if (Character.isDigit(value.charAt(i))) {
				return value.substring(i);
			}
		}
		return "";
	}


	/**
	 * Representation of a semantic version.
	 */
	public static final class Version implements Comparable {

		private final int major;

		private final int minor;

		private final int patch;

		Version(int major, int minor, int patch) {
			this.major = major;
			this.minor = minor;
			this.patch = patch;
		}

		public int getMajor() {
			return this.major;
		}

		public int getMinor() {
			return this.minor;
		}

		public int getPatch() {
			return this.patch;
		}

		@Override
		public int compareTo(SemanticApiVersionParser.Version other) {
			int result = Integer.compare(this.major, other.major);
			if (result != 0) {
				return result;
			}
			result = Integer.compare(this.minor, other.minor);
			if (result != 0) {
				return result;
			}
			return Integer.compare(this.patch, other.patch);
		}

		@Override
		public boolean equals(Object other) {
			return (this == other || (other instanceof Version otherVersion &&
					this.major == otherVersion.major &&
					this.minor == otherVersion.minor &&
					this.patch == otherVersion.patch));
		}

		@Override
		public int hashCode() {
			int result = this.major;
			result = 31 * result + this.minor;
			result = 31 * result + this.patch;
			return result;
		}

		@Override
		public String toString() {
			return this.major + "." + this.minor + "." + this.patch;
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy