blob: 22cf77504a6368b83751bc7ff9622178938c83ca [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* 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.intellij.openapi.util;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
/**
* @author max
*/
public class BuildNumber implements Comparable<BuildNumber> {
private static final String BUILD_NUMBER = "__BUILD_NUMBER__";
private static final String STAR = "*";
private static final String SNAPSHOT = "SNAPSHOT";
private static final String FALLBACK_VERSION = "999.SNAPSHOT";
private static final int TOP_BASELINE_VERSION = fromFile().getBaselineVersion();
private final String myProductCode;
private final int myBaselineVersion;
private final int myBuildNumber;
private final String myAttemptInfo;
public BuildNumber(String productCode, int baselineVersion, int buildNumber) {
this(productCode, baselineVersion, buildNumber, null);
}
public BuildNumber(String productCode, int baselineVersion, int buildNumber, String attemptInfo) {
myProductCode = productCode;
myBaselineVersion = baselineVersion;
myBuildNumber = buildNumber;
myAttemptInfo = StringUtil.isEmpty(attemptInfo) ? null : attemptInfo;
}
public String asString() {
return asString(true, false);
}
public String asStringWithoutProductCode() {
return asString(false, false);
}
private String asString(boolean includeProductCode, boolean withBuildAttempt) {
StringBuilder builder = new StringBuilder();
if (includeProductCode && !StringUtil.isEmpty(myProductCode)) {
builder.append(myProductCode).append('-');
}
builder.append(myBaselineVersion).append('.');
if (myBuildNumber != Integer.MAX_VALUE) {
builder.append(myBuildNumber);
}
else {
builder.append(SNAPSHOT);
}
if (withBuildAttempt && myAttemptInfo != null) {
builder.append('.').append(myAttemptInfo);
}
return builder.toString();
}
public static BuildNumber fromString(String version) {
return fromString(version, null);
}
public static BuildNumber fromString(String version, @Nullable String name) {
if (version == null) return null;
if (BUILD_NUMBER.equals(version)) {
final String productCode = name != null ? name : "";
return new BuildNumber(productCode, TOP_BASELINE_VERSION, Integer.MAX_VALUE);
}
String code = version;
int productSeparator = code.indexOf('-');
final String productCode;
if (productSeparator > 0) {
productCode = code.substring(0, productSeparator);
code = code.substring(productSeparator + 1);
}
else {
productCode = "";
}
int baselineVersionSeparator = code.indexOf('.');
int baselineVersion;
int buildNumber;
String attemptInfo = null;
if (baselineVersionSeparator > 0) {
try {
String baselineVersionString = code.substring(0, baselineVersionSeparator);
if (baselineVersionString.trim().isEmpty()) return null;
baselineVersion = Integer.parseInt(baselineVersionString);
code = code.substring(baselineVersionSeparator + 1);
}
catch (NumberFormatException e) {
throw new RuntimeException("Invalid version number: " + version + "; plugin name: " + name);
}
int minorBuildSeparator = code.indexOf('.'); // allow <BuildNumber>.<BuildAttemptNumber> skipping BuildAttemptNumber
if (minorBuildSeparator > 0) {
attemptInfo = code.substring(minorBuildSeparator + 1);
code = code.substring(0, minorBuildSeparator);
}
buildNumber = parseBuildNumber(version, code, name);
}
else {
buildNumber = parseBuildNumber(version, code, name);
if (buildNumber <= 2000) {
// it's probably a baseline, not a build number
return new BuildNumber(productCode, buildNumber, 0, null);
}
baselineVersion = getBaseLineForHistoricBuilds(buildNumber);
}
return new BuildNumber(productCode, baselineVersion, buildNumber, attemptInfo);
}
private static int parseBuildNumber(String version, String code, String name) {
if (SNAPSHOT.equals(code) || STAR.equals(code) || BUILD_NUMBER.equals(code)) {
return Integer.MAX_VALUE;
}
try {
return Integer.parseInt(code);
}
catch (NumberFormatException e) {
throw new RuntimeException("Invalid version number: " + version + "; plugin name: " + name);
}
}
private static BuildNumber fromFile() {
try {
final String homePath = PathManager.getHomePath();
final File buildTxtFile = FileUtil.findFirstThatExist(homePath + "/build.txt", homePath + "/community/build.txt");
if (buildTxtFile != null) {
String text = FileUtil.loadFile(buildTxtFile).trim();
return fromString(text);
}
}
catch (IOException ignored) { }
return fallback();
}
public static BuildNumber fallback() {
return fromString(FALLBACK_VERSION);
}
@Override
public String toString() {
return asString();
}
@Override
public int compareTo(@NotNull BuildNumber o) {
if (myBaselineVersion == o.myBaselineVersion) return myBuildNumber - o.myBuildNumber;
return myBaselineVersion - o.myBaselineVersion;
}
public String getProductCode() {
return myProductCode;
}
public int getBaselineVersion() {
return myBaselineVersion;
}
public int getBuildNumber() {
return myBuildNumber;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BuildNumber that = (BuildNumber)o;
if (myBaselineVersion != that.myBaselineVersion) return false;
if (myBuildNumber != that.myBuildNumber) return false;
if (!myProductCode.equals(that.myProductCode)) return false;
if (!Comparing.equal(myAttemptInfo, that.myAttemptInfo)) return false;
return true;
}
@Override
public int hashCode() {
int result = myProductCode.hashCode();
result = 31 * result + myBaselineVersion;
result = 31 * result + myBuildNumber;
if (myAttemptInfo != null) result = 31 * result + myAttemptInfo.hashCode();
return result;
}
// See http://www.jetbrains.net/confluence/display/IDEADEV/Build+Number+Ranges for historic build ranges
private static int getBaseLineForHistoricBuilds(int bn) {
if (bn == Integer.MAX_VALUE) {
return TOP_BASELINE_VERSION; // SNAPSHOTS
}
if (bn >= 10000) {
return 88; // Maia, 9x builds
}
if (bn >= 9500) {
return 85; // 8.1 builds
}
if (bn >= 9100) {
return 81; // 8.0.x builds
}
if (bn >= 8000) {
return 80; // 8.0, including pre-release builds
}
if (bn >= 7500) {
return 75; // 7.0.2+
}
if (bn >= 7200) {
return 72; // 7.0 final
}
if (bn >= 6900) {
return 69; // 7.0 pre-M2
}
if (bn >= 6500) {
return 65; // 7.0 pre-M1
}
if (bn >= 6000) {
return 60; // 6.0.2+
}
if (bn >= 5000) {
return 55; // 6.0 branch, including all 6.0 EAP builds
}
if (bn >= 4000) {
return 50; // 5.1 branch
}
return 40;
}
public boolean isSnapshot() {
return myBuildNumber == Integer.MAX_VALUE;
}
public String asStringWithAllDetails() {
return asString(true, true);
}
}