new build number format
authorMaxim Shafirov <max@jetbrains.com>
Wed, 30 Sep 2009 11:42:50 +0000 (15:42 +0400)
committerMaxim Shafirov <max@jetbrains.com>
Wed, 30 Sep 2009 11:42:50 +0000 (15:42 +0400)
build.txt [new file with mode: 0644]
platform/platform-api/src/com/intellij/ide/plugins/PluginManager.java
platform/platform-api/src/com/intellij/openapi/application/ApplicationInfo.java
platform/platform-impl/src/com/intellij/ide/plugins/PluginInstaller.java
platform/platform-impl/src/com/intellij/ide/plugins/RepositoryHelper.java
platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationInfoImpl.java
platform/util/src/com/intellij/openapi/util/BuildNumber.java [new file with mode: 0644]
platform/util/testSrc/com/intellij/openapi/util/BuildNumberTest.java [new file with mode: 0644]

diff --git a/build.txt b/build.txt
new file mode 100644 (file)
index 0000000..b478d31
--- /dev/null
+++ b/build.txt
@@ -0,0 +1 @@
+90.SNAPSHOT
index 897b8dafa010ca87919289520c9f249b1f3bbf92..2622bb24fccfbe647eb52720f0f297bee751375d 100644 (file)
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2000-2009 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.ide.plugins;
 
 import com.intellij.ide.ClassloaderUtil;
@@ -13,6 +29,7 @@ import com.intellij.openapi.extensions.Extensions;
 import com.intellij.openapi.extensions.LogProvider;
 import com.intellij.openapi.extensions.PluginId;
 import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.util.BuildNumber;
 import com.intellij.openapi.util.Comparing;
 import com.intellij.openapi.util.Condition;
 import com.intellij.openapi.util.io.FileUtil;
@@ -56,7 +73,7 @@ public class PluginManager {
 
   static final Object lock = new Object();
 
-  private static String ourBuildNumber;
+  private static BuildNumber ourBuildNumber;
   @NonNls public static final String PLUGIN_XML = "plugin.xml";
   @NonNls public static final String META_INF = "META-INF";
   private static final Map<PluginId,Integer> ourId2Index = new THashMap<PluginId, Integer>();
@@ -381,37 +398,20 @@ public class PluginManager {
   }
 
   public static boolean isIncompatible(final IdeaPluginDescriptor descriptor) {
-    final String buildNumberString = getBuildNumber();
-    if (buildNumberString != null) {
-      int buildNumber;
-      try {
-        buildNumber = Integer.parseInt(buildNumberString);
-      }
-      catch (NumberFormatException e) {
-        return false;
-      }
-      final String sinceBuild = descriptor.getSinceBuild();
-      try {
-        int sinceBuildNumber = Integer.parseInt(sinceBuild);
-        if (sinceBuildNumber > buildNumber) {
-          return true;
-        }
-      }
-      catch (NumberFormatException e) {
-        //skip invalid numbers
-      }
+    BuildNumber buildNumber = getBuildNumber();
 
-      final String untilBuild = descriptor.getUntilBuild();
-      try {
-        int untilBuildNumber = Integer.parseInt(untilBuild);
-        if (untilBuildNumber < buildNumber) {
-          return true;
-        }
-      }
-      catch (NumberFormatException e) {
-        //skip invalid numbers
+    if (descriptor.getSinceBuild() != null) {
+      BuildNumber sinceBuild = BuildNumber.fromString(descriptor.getSinceBuild());
+      if (sinceBuild.compareTo(buildNumber) > 0) {
+        return true;
       }
     }
+
+    if (descriptor.getUntilBuild() != null && !buildNumber.isSnapshot()) {
+      BuildNumber untilBuild = BuildNumber.fromString(descriptor.getUntilBuild());
+      if (untilBuild.compareTo(buildNumber) < 0) return true;
+    }
+
     return false;
   }
 
@@ -687,16 +687,23 @@ public class PluginManager {
     return true;
   }
 
-  @Nullable
-  static String getBuildNumber() {
+  static BuildNumber getBuildNumber() {
     if (ourBuildNumber == null) {
-      ourBuildNumber = System.getProperty("idea.plugins.compatible.build");
+      ourBuildNumber = BuildNumber.fromString(System.getProperty("idea.plugins.compatible.build"));
       if (ourBuildNumber == null) {
         try {
-          ourBuildNumber = new String(FileUtil.loadFileText(new File(PathManager.getHomePath() + "/build.txt"))).trim();
+          File buildTxtFile =
+            FileUtil.findFirstThatExist(PathManager.getHomePath() + "/build.txt", PathManager.getHomePath() + "/community/build.txt");
+
+          if (buildTxtFile != null) {
+            ourBuildNumber = BuildNumber.fromString(new String(FileUtil.loadFileText(buildTxtFile)).trim());
+          }
+          else {
+            ourBuildNumber = BuildNumber.fromString("90.SNAPSHOT");
+          }
         }
         catch (IOException e) {
-          ourBuildNumber = null;
+          ourBuildNumber = BuildNumber.fromString("90.SNAPSHOT");
         }
       }
     }
index dcb7f841669f2a05abffb4115eb40a2db0031ea7..6ad58e349151513eb0f3b773d58fac1c47b2677d 100644 (file)
@@ -1,25 +1,37 @@
 /*
- * Copyright 2000-2007 JetBrains s.r.o.
+ * Copyright 2000-2009 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
+ *  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
+ *  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.
+ *  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.application;
 
+import com.intellij.openapi.util.BuildNumber;
+
 import java.util.Calendar;
 
 public abstract class ApplicationInfo {
   public abstract Calendar getBuildDate();
-  public abstract String getBuildNumber();
+
+  @Deprecated()
+  /**
+   * Use {@link #getBuild()} instead
+   */
+  public String getBuildNumber() {
+    return getBuild().asString();
+  }
+
+  public abstract BuildNumber getBuild();
+
   public abstract String getMajorVersion();
   public abstract String getMinorVersion();
   public abstract String getVersionName();
index 2c9b8065a449480d42f6602854c974e229aaf87c..bbf8c59331040fb4c204686fc60a0f02625e8696 100644 (file)
@@ -9,6 +9,7 @@ import com.intellij.openapi.progress.ProgressManager;
 import com.intellij.openapi.ui.DialogWrapper;
 import com.intellij.openapi.ui.Messages;
 import com.intellij.openapi.updateSettings.impl.PluginDownloader;
+import com.intellij.openapi.util.BuildNumber;
 import com.intellij.ui.GuiUtils;
 import org.jetbrains.annotations.NonNls;
 
@@ -138,10 +139,10 @@ public class PluginInstaller {
     }
 
     synchronized (PluginManager.lock) {
-      final String buildNumber = RepositoryHelper.extractBuildNumber();
+      final BuildNumber buildNumber = PluginManager.getBuildNumber();
       final @NonNls String url = RepositoryHelper.DOWNLOAD_URL +
                          URLEncoder.encode(pluginNode.getPluginId().getIdString(), "UTF8") +
-                         "&build=" + buildNumber;
+                         "&build=" + buildNumber.asString();
       final PluginDownloader downloader =
         new PluginDownloader(pluginNode.getPluginId().getIdString(), url, null, null, pluginNode.getName());
       if (downloader.prepareToInstall(ProgressManager.getInstance().getProgressIndicator())) {
index 59bae100452ef79bc6574f21870fbf441df0e549..efcff2d5cd9d0b4fd2ef4069fa4aeef06d4adc16 100644 (file)
@@ -3,6 +3,7 @@ package com.intellij.ide.plugins;
 import com.intellij.ide.IdeBundle;
 import com.intellij.openapi.application.PathManager;
 import com.intellij.openapi.application.impl.ApplicationInfoImpl;
+import com.intellij.openapi.util.BuildNumber;
 import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.util.net.HttpConfigurable;
 import com.intellij.util.ui.UIUtil;
@@ -38,8 +39,8 @@ public class RepositoryHelper {
   public static ArrayList<IdeaPluginDescriptor> process(JLabel label) throws IOException, ParserConfigurationException, SAXException {
     ArrayList<IdeaPluginDescriptor> plugins = null;
     try {
-      String buildNumber = extractBuildNumber();
-      @NonNls String url = getListUrl() + "?build=" + buildNumber;
+      BuildNumber buildNumber = PluginManager.getBuildNumber();
+      @NonNls String url = getListUrl() + "?build=" + buildNumber.asString();
 
       setLabelText(label, IdeBundle.message("progress.connecting.to.plugin.manager", getRepositoryHost()));
       HttpConfigurable.getInstance().prepareURL(getRepositoryHost());
@@ -123,17 +124,6 @@ public class RepositoryHelper {
     }
   }
 
-  public static String extractBuildNumber() {
-    String build;
-    try {
-      build = Integer.valueOf(PluginManager.getBuildNumber()).toString();
-    }
-    catch (NumberFormatException e) {
-      build = "3000";
-    }
-    return build;
-  }
-
   public static String getRepositoryHost() {
     return ApplicationInfoImpl.getShadowInstance().getPluginManagerUrl();
   }
index 33ba55c7b1009bff6c4b057e3b446c07b328e0d3..86e0e772e3ccf21a6282354b53636de515a90b67 100644 (file)
@@ -6,10 +6,7 @@ import com.intellij.openapi.application.PathManager;
 import com.intellij.openapi.application.ex.ApplicationInfoEx;
 import com.intellij.openapi.components.ApplicationComponent;
 import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.util.InvalidDataException;
-import com.intellij.openapi.util.JDOMExternalizable;
-import com.intellij.openapi.util.JDOMUtil;
-import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.openapi.util.*;
 import org.jdom.Document;
 import org.jdom.Element;
 import org.jetbrains.annotations.NonNls;
@@ -107,8 +104,9 @@ public class ApplicationInfoImpl extends ApplicationInfoEx implements JDOMExtern
     return myBuildDate;
   }
 
-  public String getBuildNumber() {
-    return myBuildNumber;
+  @Override
+  public BuildNumber getBuild() {
+    return BuildNumber.fromString(myBuildNumber);
   }
 
   public String getMajorVersion() {
diff --git a/platform/util/src/com/intellij/openapi/util/BuildNumber.java b/platform/util/src/com/intellij/openapi/util/BuildNumber.java
new file mode 100644 (file)
index 0000000..747eec7
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * @author max
+ */
+package com.intellij.openapi.util;
+
+import com.intellij.openapi.util.text.StringUtil;
+
+public class BuildNumber implements Comparable<BuildNumber> {
+  private final String myProductCode;
+  private final int myBaselineVersion;
+  private final int myBuildNumber;
+
+  public BuildNumber(String productCode, int baselineVersion, int buildNumber) {
+    myProductCode = productCode;
+    myBaselineVersion = baselineVersion;
+    myBuildNumber = buildNumber;
+  }
+
+  public String asString() {
+    StringBuilder builder = new StringBuilder();
+    if (!StringUtil.isEmpty(myProductCode)) {
+      builder.append(myProductCode).append('-');
+    }
+
+    builder.append(myBaselineVersion).append('.');
+
+    if (myBuildNumber != Integer.MAX_VALUE) {
+      builder.append(myBuildNumber);
+    }
+    else {
+      builder.append("SNAPSHOT");
+    }
+
+    return builder.toString();
+  }
+
+  @Override
+  public String toString() {
+    return asString();
+  }
+
+  public static BuildNumber fromString(String version) {
+    if (version == null) return null;
+    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;
+    if (baselineVersionSeparator > 0) {
+      try {
+        baselineVersion = Integer.parseInt(code.substring(0, baselineVersionSeparator));
+        code = code.substring(baselineVersionSeparator + 1);
+      }
+      catch (NumberFormatException e) {
+        throw new RuntimeException("Unparseable version number: " + version);
+      }
+
+      if ("SNAPSHOT".equals(code) || "__BUILD_NUMBER__".equals(code)) {
+        buildNumber = Integer.MAX_VALUE;
+      }
+      else {
+        try {
+          buildNumber = Integer.parseInt(code);
+        }
+        catch (NumberFormatException e) {
+          throw new RuntimeException("Unparseable version number: " + version);
+        }
+      }
+    }
+    else {
+      try {
+        buildNumber = Integer.parseInt(code);
+      }
+      catch (NumberFormatException e) {
+        throw new RuntimeException("Unparseable version number: " + version);
+      }
+
+      if (buildNumber <= 2000) {
+        // it's probably a baseline, not a build number
+        return new BuildNumber(productCode, buildNumber, 0);
+      }
+
+      baselineVersion = getBaseLineForHistoricBuilds(buildNumber);
+    }
+
+    return new BuildNumber(productCode, baselineVersion, buildNumber);
+  }
+
+  public int compareTo(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;
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    int result = myProductCode.hashCode();
+    result = 31 * result + myBaselineVersion;
+    result = 31 * result + myBuildNumber;
+    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 >= 10000) {
+      return 90; // 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;
+  }
+}
diff --git a/platform/util/testSrc/com/intellij/openapi/util/BuildNumberTest.java b/platform/util/testSrc/com/intellij/openapi/util/BuildNumberTest.java
new file mode 100644 (file)
index 0000000..77666ac
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * @author max
+ */
+package com.intellij.openapi.util;
+
+import junit.framework.TestCase;
+
+public class BuildNumberTest extends TestCase {
+  public void testHistoricBuild() {
+    assertEquals(new BuildNumber("", 75, 7512), BuildNumber.fromString("7512"));
+  }
+  
+  public void testSnapshotDominates() {
+    assertTrue(BuildNumber.fromString("90.SNAPSHOT").compareTo(BuildNumber.fromString("90.12345")) > 0);
+    assertTrue(BuildNumber.fromString("IU-90.SNAPSHOT").compareTo(BuildNumber.fromString("RM-90.12345")) > 0);
+  }
+}