stub for ordered scopes problem
[idea/community.git] / platform / util / src / com / intellij / openapi / util / BuildNumber.java
1 /*
2  * Copyright 2000-2013 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.openapi.util;
17
18 import com.intellij.openapi.application.PathManager;
19 import com.intellij.openapi.util.io.FileUtil;
20 import com.intellij.openapi.util.text.StringUtil;
21 import org.jetbrains.annotations.NotNull;
22 import org.jetbrains.annotations.Nullable;
23
24 import java.io.File;
25 import java.io.IOException;
26
27 /**
28  * @author max
29  */
30 public class BuildNumber implements Comparable<BuildNumber> {
31   private static final String BUILD_NUMBER = "__BUILD_NUMBER__";
32   private static final String STAR = "*";
33   private static final String SNAPSHOT = "SNAPSHOT";
34   private static final String FALLBACK_VERSION = "999.SNAPSHOT";
35
36   private static final int TOP_BASELINE_VERSION = fromFile().getBaselineVersion();
37
38   private final String myProductCode;
39   private final int myBaselineVersion;
40   private final int myBuildNumber;
41   private final String myAttemptInfo;
42
43   public BuildNumber(String productCode, int baselineVersion, int buildNumber) {
44     this(productCode, baselineVersion, buildNumber, null);
45   }
46
47   public BuildNumber(String productCode, int baselineVersion, int buildNumber, String attemptInfo) {
48     myProductCode = productCode;
49     myBaselineVersion = baselineVersion;
50     myBuildNumber = buildNumber;
51     myAttemptInfo = StringUtil.isEmpty(attemptInfo) ? null : attemptInfo;
52   }
53
54   public String asString() {
55     return asString(true);
56   }
57
58   public String asStringWithoutProductCode() {
59     return asString(false);
60   }
61
62   private String asString(boolean includeProductCode) {
63     StringBuilder builder = new StringBuilder();
64
65     if (includeProductCode && !StringUtil.isEmpty(myProductCode)) {
66       builder.append(myProductCode).append('-');
67     }
68
69     builder.append(myBaselineVersion).append('.');
70
71     if (myBuildNumber != Integer.MAX_VALUE) {
72       builder.append(myBuildNumber);
73     }
74     else {
75       builder.append(SNAPSHOT);
76     }
77
78     if (myAttemptInfo != null) {
79       builder.append('.').append(myAttemptInfo);
80     }
81
82     return builder.toString();
83   }
84
85   public static BuildNumber fromString(String version) {
86     return fromString(version, null);
87   }
88
89   public static BuildNumber fromString(String version, @Nullable String name) {
90     if (version == null) return null;
91
92     if (BUILD_NUMBER.equals(version)) {
93       final String productCode = name != null ? name : "";
94       return new BuildNumber(productCode, TOP_BASELINE_VERSION, Integer.MAX_VALUE);
95     }
96
97     String code = version;
98     int productSeparator = code.indexOf('-');
99     final String productCode;
100     if (productSeparator > 0) {
101       productCode = code.substring(0, productSeparator);
102       code = code.substring(productSeparator + 1);
103     }
104     else {
105       productCode = "";
106     }
107
108     int baselineVersionSeparator = code.indexOf('.');
109     int baselineVersion;
110     int buildNumber;
111     String attemptInfo = null;
112
113     if (baselineVersionSeparator > 0) {
114       try {
115         String baselineVersionString = code.substring(0, baselineVersionSeparator);
116         if (baselineVersionString.trim().isEmpty()) return null;
117         baselineVersion = Integer.parseInt(baselineVersionString);
118         code = code.substring(baselineVersionSeparator + 1);
119       }
120       catch (NumberFormatException e) {
121         throw new RuntimeException("Invalid version number: " + version + "; plugin name: " + name);
122       }
123
124       int minorBuildSeparator = code.indexOf('.'); // allow <BuildNumber>.<BuildAttemptNumber> skipping BuildAttemptNumber
125       if (minorBuildSeparator > 0) {
126         attemptInfo = code.substring(minorBuildSeparator + 1);
127         code = code.substring(0, minorBuildSeparator);
128       }
129       buildNumber = parseBuildNumber(version, code, name);
130     }
131     else {
132       buildNumber = parseBuildNumber(version, code, name);
133
134       if (buildNumber <= 2000) {
135         // it's probably a baseline, not a build number
136         return new BuildNumber(productCode, buildNumber, 0, null);
137       }
138
139       baselineVersion = getBaseLineForHistoricBuilds(buildNumber);
140     }
141
142     return new BuildNumber(productCode, baselineVersion, buildNumber, attemptInfo);
143   }
144
145   private static int parseBuildNumber(String version, String code, String name) {
146     if (SNAPSHOT.equals(code) || STAR.equals(code) || BUILD_NUMBER.equals(code)) {
147       return Integer.MAX_VALUE;
148     }
149     try {
150       return Integer.parseInt(code);
151     }
152     catch (NumberFormatException e) {
153       throw new RuntimeException("Invalid version number: " + version + "; plugin name: " + name);
154     }
155   }
156
157   private static BuildNumber fromFile() {
158     try {
159       final String homePath = PathManager.getHomePath();
160       final File buildTxtFile = FileUtil.findFirstThatExist(homePath + "/build.txt", homePath + "/community/build.txt");
161       if (buildTxtFile != null) {
162         String text = FileUtil.loadFile(buildTxtFile).trim();
163         return fromString(text);
164       }
165     }
166     catch (IOException ignored) { }
167
168     return fallback();
169   }
170
171   public static BuildNumber fallback() {
172     return fromString(FALLBACK_VERSION);
173   }
174
175   @Override
176   public String toString() {
177     return asString();
178   }
179
180   @Override
181   public int compareTo(@NotNull BuildNumber o) {
182     if (myBaselineVersion == o.myBaselineVersion) return myBuildNumber - o.myBuildNumber;
183     return myBaselineVersion - o.myBaselineVersion;
184   }
185
186   public String getProductCode() {
187     return myProductCode;
188   }
189
190   public int getBaselineVersion() {
191     return myBaselineVersion;
192   }
193
194   public int getBuildNumber() {
195     return myBuildNumber;
196   }
197
198   @Override
199   public boolean equals(Object o) {
200     if (this == o) return true;
201     if (o == null || getClass() != o.getClass()) return false;
202
203     BuildNumber that = (BuildNumber)o;
204
205     if (myBaselineVersion != that.myBaselineVersion) return false;
206     if (myBuildNumber != that.myBuildNumber) return false;
207     if (!myProductCode.equals(that.myProductCode)) return false;
208     if (!Comparing.equal(myAttemptInfo, that.myAttemptInfo)) return false;
209
210     return true;
211   }
212
213   @Override
214   public int hashCode() {
215     int result = myProductCode.hashCode();
216     result = 31 * result + myBaselineVersion;
217     result = 31 * result + myBuildNumber;
218     if (myAttemptInfo != null) result = 31 * result + myAttemptInfo.hashCode();
219     return result;
220   }
221
222   // See http://www.jetbrains.net/confluence/display/IDEADEV/Build+Number+Ranges for historic build ranges
223   private static int getBaseLineForHistoricBuilds(int bn) {
224     if (bn == Integer.MAX_VALUE) {
225       return TOP_BASELINE_VERSION; // SNAPSHOTS
226     }
227
228     if (bn >= 10000) {
229       return 88; // Maia, 9x builds
230     }
231
232     if (bn >= 9500) {
233       return 85; // 8.1 builds
234     }
235
236     if (bn >= 9100) {
237       return 81; // 8.0.x builds
238     }
239
240     if (bn >= 8000) {
241       return 80; // 8.0, including pre-release builds
242     }
243
244     if (bn >= 7500) {
245       return 75; // 7.0.2+
246     }
247
248     if (bn >= 7200) {
249       return 72; // 7.0 final
250     }
251
252     if (bn >= 6900) {
253       return 69; // 7.0 pre-M2
254     }
255
256     if (bn >= 6500) {
257       return 65; // 7.0 pre-M1
258     }
259
260     if (bn >= 6000) {
261       return 60; // 6.0.2+
262     }
263
264     if (bn >= 5000) {
265       return 55; // 6.0 branch, including all 6.0 EAP builds
266     }
267
268     if (bn >= 4000) {
269       return 50; // 5.1 branch
270     }
271
272     return 40;
273   }
274
275   public boolean isSnapshot() {
276     return myBuildNumber == Integer.MAX_VALUE;
277   }
278 }