Merge branch 'master' into amakeev/build-number
authorAnton Makeev <Anton.Makeev@jetbrains.com>
Thu, 7 Apr 2016 08:58:23 +0000 (10:58 +0200)
committerAnton Makeev <Anton.Makeev@jetbrains.com>
Thu, 7 Apr 2016 08:58:23 +0000 (10:58 +0200)
16 files changed:
build.txt
community-resources/src/idea/IdeaApplicationInfo.xml
platform/built-in-server/src/org/jetbrains/ide/AboutHttpService.java
platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java
platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java
platform/core-impl/src/com/intellij/openapi/application/impl/ApplicationInfoImpl.java
platform/platform-impl/src/com/intellij/openapi/application/ApplicationInfoUtil.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateInfoDialog.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java
platform/platform-tests/testSrc/com/intellij/ide/plugins/PluginManagerTest.java [new file with mode: 0644]
platform/util/src/com/intellij/openapi/util/BuildNumber.java
platform/util/testSrc/com/intellij/openapi/util/BuildNumberTest.java
plugins/eclipse/src/org/jetbrains/idea/eclipse/importWizard/EclipseImportBuilder.java
plugins/settings-repository/src/git/CommitMessageFormatter.kt
python/educational-python/resources/idea/PyCharmEduApplicationInfo.xml
python/python-community-ide-resources/resources/idea/PyCharmCoreApplicationInfo.xml

index 6f540e14d3a67c3387c999df8cdb42cb47a65252..3cc9852876ab295dadc15e999ff90c9f4ed3fcee 100644 (file)
--- a/build.txt
+++ b/build.txt
@@ -1 +1 @@
-146.SNAPSHOT
+2016.2.SNAPSHOT
index 500ea220abffd8d5c62827d0cafbd59219a62c9a..a78f5cae00fd2a4cb4d6ae0d5271aa6fd6c97ab8 100644 (file)
@@ -1,5 +1,5 @@
 <component>
-  <version codename="Community Edition 2016.2" major="2016" minor="2" eap="true"/>
+  <version codename="Community Edition" major="2016" minor="2" eap="true"/>
   <company name="JetBrains s.r.o." url="https://www.jetbrains.com/?fromIDE"/>
   <build number="__BUILD_NUMBER__" date="__BUILD_DATE__"/>
   <logo url="/idea_community_logo.png" textcolor="444444" progressColor="f87206" progressX="0" progressY="271" progressHeight="3" licenseOffsetY="30" />
index 264dc890663bf52fb04d200169934e6ba5a94595..1b58b5cb83088b666254cd3e49c6b4dd4664d0fb 100644 (file)
 package org.jetbrains.ide;
 
 import com.google.gson.stream.JsonWriter;
-import com.intellij.openapi.application.ApplicationInfo;
-import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.application.ApplicationInfoUtil;
 import com.intellij.openapi.application.PathManager;
 import com.intellij.openapi.application.ex.ApplicationInfoEx;
 import com.intellij.openapi.fileTypes.FileType;
 import com.intellij.openapi.fileTypes.FileTypeRegistry;
-import com.intellij.openapi.util.BuildNumber;
 import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.util.PlatformUtils;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.handler.codec.http.FullHttpRequest;
 import io.netty.handler.codec.http.HttpMethod;
@@ -83,25 +79,10 @@ public class AboutHttpService extends RestService {
   }
 
   public static void getAbout(@NotNull OutputStream out, @Nullable QueryStringDecoder urlDecoder) throws IOException {
-    BuildNumber build = ApplicationInfo.getInstance().getBuild();
-
     JsonWriter writer = createJsonWriter(out);
     writer.beginObject();
 
-    ApplicationInfoEx appInfo = ApplicationInfoEx.getInstanceEx();
-    String appName = appInfo.getFullApplicationName();
-    if (!PlatformUtils.isIdeaUltimate()) {
-      String productName = ApplicationNamesInfo.getInstance().getProductName();
-      appName = appName.replace(productName + " (" + productName + ")", productName);
-      appName = StringUtil.trimStart(appName, "JetBrains ");
-    }
-
-    writer.name("name").value(appName);
-    writer.name("productName").value(ApplicationNamesInfo.getInstance().getProductName());
-    writer.name("baselineVersion").value(build.getBaselineVersion());
-    if (build.getBuildNumber() != Integer.MAX_VALUE) {
-      writer.name("buildNumber").value(build.getBuildNumber());
-    }
+    ApplicationInfoUtil.writeJson(writer);
 
     if (urlDecoder != null && getBooleanParameter("registeredFileTypes", urlDecoder)) {
       writer.name("registeredFileTypes").beginArray();
@@ -116,6 +97,7 @@ public class AboutHttpService extends RestService {
     }
 
     if (urlDecoder != null && getBooleanParameter("more", urlDecoder)) {
+      ApplicationInfoEx appInfo = ApplicationInfoEx.getInstanceEx();
       writer.name("vendor").value(appInfo.getCompanyName());
       writer.name("isEAP").value(appInfo.isEAP());
       writer.name("productCode").value(appInfo.getBuild().getProductCode());
index e28d31d5db8101217f4faa33cb813c3116fda6a8..e3ad45c7d100f68829206ffa674b908218bc8dfc 100644 (file)
@@ -226,7 +226,7 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor {
     myChangeNotes = pluginBean.changeNotes;
     myVersion = pluginBean.pluginVersion;
     if (myVersion == null) {
-      myVersion = PluginManagerCore.getBuildNumber().getBaselineVersion() + ".SNAPSHOT";
+      myVersion = PluginManagerCore.getBuildNumber().asStringWithoutProductCode();
     }
 
     myCategory = pluginBean.category;
index 2ef795bcc7f750da940e9803f53d352b0846a5bf..af8ff4e25860f90673cd96bee800c6ee82a03401 100644 (file)
@@ -1110,25 +1110,39 @@ public class PluginManagerCore {
     }
 
     try {
-      if (!StringUtil.isEmpty(descriptor.getSinceBuild())) {
-        BuildNumber sinceBuild = BuildNumber.fromString(descriptor.getSinceBuild(), descriptor.getName());
-        if (sinceBuild.compareTo(buildNumber) > 0) {
-          LOG.warn("Can't load " + descriptor + ": since build " + sinceBuild + " does not match " + buildNumber);
-          return true;
+      return isIncompatible(buildNumber, descriptor.getSinceBuild(), descriptor.getUntilBuild(), 
+                            descriptor.getName(), descriptor.toString());
+    }
+    catch (RuntimeException e) {
+      LOG.error(e);
+    }
+
+    return false;
+  }
+
+  public static boolean isIncompatible(@NotNull BuildNumber buildNumber, @Nullable String sinceBuild, @Nullable String untilBuild,
+                                       @Nullable String descriptorName,
+                                       @Nullable String descriptorDebugString) {
+
+    if (!StringUtil.isEmpty(sinceBuild)) {
+      BuildNumber sinceBuildNumber = BuildNumber.fromString(sinceBuild, descriptorName);
+      if (sinceBuildNumber.compareTo(buildNumber) > 0) {
+        if (descriptorDebugString != null) {
+          LOG.warn("Can't load " + descriptorDebugString + ": since build " + sinceBuildNumber + " does not match " + buildNumber);
         }
+        return true;
       }
+    }
 
-      if (!StringUtil.isEmpty(descriptor.getUntilBuild()) && !buildNumber.isSnapshot()) {
-        BuildNumber untilBuild = BuildNumber.fromString(descriptor.getUntilBuild(), descriptor.getName());
-        if (untilBuild.compareTo(buildNumber) < 0) {
-          LOG.warn("Can't load " + descriptor + ": until build " + untilBuild + " does not match " + buildNumber);
-          return true;
+    if (!StringUtil.isEmpty(untilBuild) && !buildNumber.isSnapshot()) {
+      BuildNumber untilBuildNumber = BuildNumber.fromString(untilBuild, descriptorName);
+      if (untilBuildNumber.compareTo(buildNumber) < 0) {
+        if (descriptorDebugString != null) {
+          LOG.warn("Can't load " + descriptorDebugString + ": until build " + untilBuildNumber + " does not match " + buildNumber);
         }
+        return true;
       }
     }
-    catch (RuntimeException e) {
-      LOG.error(e);
-    }
 
     return false;
   }
index 981d76ec7e3602057b04d4fcbbce79c65e0a7740..30e81f2b741136bd1585a3c138316ab917d0abd9 100644 (file)
@@ -262,6 +262,12 @@ public class ApplicationInfoImpl extends ApplicationInfoEx {
 
   @Override
   public String getFullVersion() {
+    String result = doGetFullVersion();
+    if (isEAP()) result += " EAP";
+    return result;
+  }
+
+  private String doGetFullVersion() {
     if (myFullVersion == null) {
       if (!StringUtil.isEmptyOrSpaces(myMajorVersion)) {
         if (!StringUtil.isEmptyOrSpaces(myMinorVersion)) {
@@ -503,16 +509,7 @@ public class ApplicationInfoImpl extends ApplicationInfoEx {
 
   @Override
   public String getFullApplicationName() {
-    StringBuilder buffer = new StringBuilder();
-    buffer.append(getVersionName());
-    buffer.append(" ");
-    if (getMajorVersion() != null && !isEAP() && !isBetaOrRC()) {
-      buffer.append(getFullVersion());
-    }
-    else {
-      buffer.append(getBuild().asStringWithAllDetails());
-    }
-    return buffer.toString();
+    return getVersionName() + " " + getFullVersion();
   }
 
   @Override
diff --git a/platform/platform-impl/src/com/intellij/openapi/application/ApplicationInfoUtil.java b/platform/platform-impl/src/com/intellij/openapi/application/ApplicationInfoUtil.java
new file mode 100644 (file)
index 0000000..e72f28c
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2000-2016 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.application;
+
+import com.google.gson.stream.JsonWriter;
+import com.intellij.openapi.application.ex.ApplicationInfoEx;
+import com.intellij.openapi.util.BuildNumber;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.PlatformUtils;
+
+import java.io.IOException;
+
+public class ApplicationInfoUtil {
+  public static void writeJson(JsonWriter writer) throws IOException {
+    String appName = ApplicationInfoEx.getInstanceEx().getFullApplicationName();
+    BuildNumber build = ApplicationInfo.getInstance().getBuild();
+
+    if (!PlatformUtils.isIdeaUltimate()) {
+      String productName = ApplicationNamesInfo.getInstance().getProductName();
+      appName = appName.replace(productName + " (" + productName + ")", productName);
+      appName = StringUtil.trimStart(appName, "JetBrains ");
+    }
+
+    writer.name("name").value(appName);
+    writer.name("productName").value(ApplicationNamesInfo.getInstance().getProductName());
+    writer.name("baselineVersion").value(build.getBaselineVersion());
+    if (build.getBuildNumber() != BuildNumber.SNAPSHOT_VALUE) {
+      writer.name("buildNumber").value(build.getBuildNumber());
+    }
+  }
+}
index d6aabeb8862405338be574dff69b11ae74018b9f..1a9a6fa47c8589a22880469f2e052a776430a8a8 100644 (file)
@@ -23,7 +23,6 @@ import com.intellij.openapi.application.ApplicationInfo;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.application.ApplicationNamesInfo;
 import com.intellij.openapi.application.PathManager;
-import com.intellij.openapi.application.ex.ApplicationInfoEx;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.ui.Messages;
 import com.intellij.openapi.util.text.StringUtil;
@@ -237,7 +236,7 @@ class UpdateInfoDialog extends AbstractUpdateDialog {
 
       myCurrentVersion.setText(
         formatVersion(
-          appInfo.getFullVersion() + (appInfo instanceof ApplicationInfoEx && ((ApplicationInfoEx)appInfo).isEAP() ? " EAP": ""),
+          appInfo.getFullVersion(),
           appInfo.getBuild().asStringWithoutProductCode()
         )
       );
index d3697f21fd6cfa15416d8c5b11144f0dd2edb2af..8f46883b5e00edcb6829df6c1502520d7af90b39 100644 (file)
@@ -424,12 +424,19 @@ public class FlatWelcomeFrame extends JFrame implements IdeFrame, AccessibleCont
       appName.setForeground(JBColor.foreground());
       appName.setFont(font.deriveFont(JBUI.scale(36f)).deriveFont(Font.PLAIN));
       appName.setHorizontalAlignment(SwingConstants.CENTER);
-      String appVersion = "Version " + app.getFullVersion();
+      String appVersion = "Version ";
 
-      if (app.isEAP() && app.getBuild().getBuildNumber() < Integer.MAX_VALUE) {
-        appVersion += " (" + app.getBuild().asString() + ")";
+      if (app.getBuild().getFormat() == BuildNumber.Format.YEAR_BASED) {
+        appVersion += app.isEAP() ? (app.getBuild().asStringWithoutProductCode() + " EAP") : app.getFullVersion();
       }
+      else {
+        appVersion += app.getFullVersion();
 
+        if (app.isEAP() && app.getBuild().getBuildNumber() != BuildNumber.SNAPSHOT_VALUE) {
+          appVersion += " (" + app.getBuild().asString() + ")";
+        }
+      }
+      
       JLabel version = new JLabel(appVersion);
       version.setFont(getProductFont().deriveFont(JBUI.scale(16f)));
       version.setHorizontalAlignment(SwingConstants.CENTER);
diff --git a/platform/platform-tests/testSrc/com/intellij/ide/plugins/PluginManagerTest.java b/platform/platform-tests/testSrc/com/intellij/ide/plugins/PluginManagerTest.java
new file mode 100644 (file)
index 0000000..42d8056
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2000-2016 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.openapi.util.BuildNumber;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class PluginManagerTest {
+  @Test
+  public void compatibilityBranchBased() throws Exception {
+    assertCompatible("145.2", null, null);
+    assertCompatible("145.2.2", null, null);
+
+    assertCompatible("145.2", "145", null);
+    assertCompatible("145.2", null, "146");
+    assertCompatible("145.2.2", "145", null);
+    assertCompatible("145.2.2", null, "146");
+    assertIncompatible("145.2", null, "145");
+
+    assertIncompatible("145.2", "146", null);
+    assertIncompatible("145.2", null, "144");
+    assertIncompatible("145.2.2", "146", null);
+    assertIncompatible("145.2.2", null, "144");
+
+    assertCompatible("145.2", "145.2", null);
+    assertCompatible("145.2", null, "145.2");
+    assertCompatible("145.2.2", "145.2", null);
+    assertIncompatible("145.2.2", null, "145.2");
+
+    assertIncompatible("145.2", "145.3", null);
+    assertIncompatible("145.2", null, "145.1");
+    assertIncompatible("145.2.2", "145.3", null);
+    assertIncompatible("145.2.2", null, "145.1");
+
+    assertCompatible("145.2", "140.3", null);
+    assertCompatible("145.2", null, "146.1");
+    assertCompatible("145.2.2", "140.3", null);
+    assertCompatible("145.2.2", null, "146.1");
+
+    assertIncompatible("145.2", "145.2.0", null);
+    assertIncompatible("145.2", "145.2.1", null);
+    assertCompatible("145.2", null, "145.2.3");
+    assertCompatible("145.2.2", "145.2.0", null);
+    assertCompatible("145.2.2", null, "145.2.3");
+  }
+
+  @Test
+  public void compatibilityBranchBasedStar() throws Exception {
+    assertCompatible("145.10", "144.*", null);
+    assertIncompatible("145.10", "145.*", null);
+    assertIncompatible("145.10", "146.*", null);
+    assertIncompatible("145.10", null, "144.*");
+    assertCompatible("145.10", null, "145.*");
+    assertCompatible("145.10", null, "146.*");
+    
+    assertCompatible("145.10.1", null, "145.*");
+    assertCompatible("145.10.1", "145.10", "145.10.*");
+
+    assertCompatible("145.SNAPSHOT", null, "145.*");
+  }
+  
+  @Test
+  public void compatibilityYearBasedStar() throws Exception {
+    assertCompatible("2016.2", "2016.1.*", null);
+    assertIncompatible("2016.2", "2016.2.*", null);
+    assertIncompatible("2016.2", "2016.2.*", null);
+    assertIncompatible("2016.2", null, "2016.1.*");
+    assertCompatible("2016.2", null, "2016.2.*");
+    assertCompatible("2016.2", null, "20163.*");
+    
+    assertCompatible("2016.2.1", null, "2016.2.*");
+    assertCompatible("2016.2.1", "2016.2", "2016.2.*");
+
+    assertCompatible("2016.2.SNAPSHOT", null, "2016.2.*");
+
+    assertCompatible("2016.2", "2016", null);
+    assertCompatible("2015", null, "2016");
+    assertIncompatible("2016.1", null, "2016");
+  }
+
+  @Test
+  public void compatibilityBranchBasedSnapshots() throws Exception {
+    assertIncompatible("145.SNAPSHOT", "146", null);
+    assertIncompatible("145.2.SNAPSHOT", "145.3", null);
+
+    assertCompatible("145.SNAPSHOT", "145.2", null);
+
+    // snapshot ignore until build (special case)
+    assertCompatible("145.SNAPSHOT", null, "145");
+    assertCompatible("145.SNAPSHOT", null, "144");
+    assertCompatible("145.2.SNAPSHOT", null, "145");
+    assertCompatible("145.2.SNAPSHOT", null, "144");
+  }
+
+  @Test
+  public void compatibilityYearBasedSnapshots() throws Exception {
+    assertIncompatible("2016.2.SNAPSHOT", "2016.3", null);
+    assertIncompatible("2016.2.2.SNAPSHOT", "2016.2.3", null);
+
+    assertCompatible("2016.2.SNAPSHOT", "2016.2.2", null);
+
+    // snapshot ignore until build (special case)
+    assertCompatible("2016.2.SNAPSHOT", null, "2016.2");
+    assertCompatible("2016.2.SNAPSHOT", null, "2016.1");
+    assertCompatible("2016.2.SNAPSHOT", null, "2017.2");
+    assertCompatible("2016.2.2.SNAPSHOT", null, "2016.2");
+    assertCompatible("2016.2.2.SNAPSHOT", null, "2016.1");
+    assertCompatible("2016.2.2.SNAPSHOT", null, "144");
+  }
+
+  private static void assertIncompatible(String ideVersion, String sinceBuild, String untilBuild) {
+    assertTrue(PluginManagerCore.isIncompatible(BuildNumber.fromString(ideVersion), sinceBuild, untilBuild, null, null));
+  }
+
+  private static void assertCompatible(String ideVersion, String sinceBuild, String untilBuild) {
+    assertFalse(PluginManagerCore.isIncompatible(BuildNumber.fromString(ideVersion), sinceBuild, untilBuild, null, null));
+  }
+}
index 36e0f2c5583c6bb5b23a4a079b37f0a23be3877d..73efdf0a16ffadb544968e41f495829f139141ad 100644 (file)
@@ -23,63 +23,73 @@ import org.jetbrains.annotations.Nullable;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * @author max
  */
 public class BuildNumber implements Comparable<BuildNumber> {
+  public enum Format { HISTORIC, BRANCH_BASED, YEAR_BASED }
+  
   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 String FALLBACK_VERSION = "2999.1.SNAPSHOT";
+
+  public static final int SNAPSHOT_VALUE = Integer.MAX_VALUE;
 
   private static class Holder {
-    private static final int TOP_BASELINE_VERSION = fromFile().getBaselineVersion();
+    private static final BuildNumber CURRENT_VERSION = fromFile();
   }
 
-  private final String myProductCode;
-  private final int myBaselineVersion;
-  private final int myBuildNumber;
-  private final String myAttemptInfo;
-
+  @NotNull  private final String myProductCode;
+  @NotNull  private final Format myFormat;
+  private final int[] myComponents;
+  
   public BuildNumber(@NotNull String productCode, int baselineVersion, int buildNumber) {
-    this(productCode, baselineVersion, buildNumber, null);
+    this(productCode, Format.BRANCH_BASED, baselineVersion, buildNumber);
   }
 
-  public BuildNumber(@NotNull String productCode, int baselineVersion, int buildNumber, @Nullable String attemptInfo) {
+  BuildNumber(@NotNull String productCode, @NotNull Format format, int... components) {
     myProductCode = productCode;
-    myBaselineVersion = baselineVersion;
-    myBuildNumber = buildNumber;
-    myAttemptInfo = StringUtil.isEmpty(attemptInfo) ? null : attemptInfo;
+    myFormat = format;
+    myComponents = components;
   }
 
   public String asString() {
-    return asString(true, false);
+    return asString(true, true);
+  }
+
+  public String asStringWithAllDetails() {
+    return asString(true, true);
   }
 
   public String asStringWithoutProductCode() {
+    return asString(false, true);
+  }
+
+  public String asStringWithoutProductCodeAndSnapshot() {
     return asString(false, false);
   }
 
-  private String asString(boolean includeProductCode, boolean withBuildAttempt) {
+  private String asString(boolean includeProductCode, boolean withSnapshotMarker) {
     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);
+    for (int each : myComponents) {
+      if (each != SNAPSHOT_VALUE) {
+        builder.append(each);
+      }
+      else if (withSnapshotMarker) {
+        builder.append(SNAPSHOT);
+      }
+      builder.append('.');
     }
+    if (builder.charAt(builder.length() - 1) == '.') builder.setLength(builder.length() - 1);
 
     return builder.toString();
   }
@@ -91,9 +101,9 @@ public class BuildNumber implements Comparable<BuildNumber> {
   public static BuildNumber fromString(String version, @Nullable String name) {
     if (StringUtil.isEmptyOrSpaces(version)) return null;
 
-    if (BUILD_NUMBER.equals(version)) {
+    if (BUILD_NUMBER.equals(version) || SNAPSHOT.equals(version)) {
       final String productCode = name != null ? name : "";
-      return new BuildNumber(productCode, Holder.TOP_BASELINE_VERSION, Integer.MAX_VALUE);
+      return new BuildNumber(productCode, Holder.CURRENT_VERSION.getFormat(), Holder.CURRENT_VERSION.myComponents);
     }
 
     String code = version;
@@ -110,44 +120,71 @@ public class BuildNumber implements Comparable<BuildNumber> {
     int baselineVersionSeparator = code.indexOf('.');
     int baselineVersion;
     int buildNumber;
-    String attemptInfo = null;
 
     if (baselineVersionSeparator > 0) {
+      String baselineVersionString = code.substring(0, baselineVersionSeparator);
+      if (baselineVersionString.trim().isEmpty()) return null;
       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);
+      if (baselineVersion >= 2016) {
+        List<String> stringComponents = StringUtil.split(code, ".");
+        int[] intComponents = new int[stringComponents.size()];
+        for (int i = 0; i < stringComponents.size(); i++) {
+          intComponents[i] = parseBuildNumber(version, stringComponents.get(i), name);
+        }
+
+        return new BuildNumber(productCode, Format.YEAR_BASED, intComponents);
+      }
+      else {
+        code = code.substring(baselineVersionSeparator + 1);
+
+        int minorBuildSeparator = code.indexOf('.'); // allow <BuildNumber>.<BuildAttemptNumber> skipping BuildAttemptNumber
+
+        Integer attemptInfo = null;
+        if (minorBuildSeparator > 0) {
+          attemptInfo = parseBuildNumber(version, code.substring(minorBuildSeparator + 1), name);
+          code = code.substring(0, minorBuildSeparator);
+        }
+        buildNumber = parseBuildNumber(version, code, name);
+
+        if (attemptInfo != null) {
+          return new BuildNumber(productCode, Format.BRANCH_BASED, baselineVersion, buildNumber, attemptInfo);
+        }
+        else {
+          return new BuildNumber(productCode, Format.BRANCH_BASED, baselineVersion, buildNumber);
+        }
       }
-      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);
+        return new BuildNumber(productCode, Format.BRANCH_BASED, buildNumber, 0);
       }
 
+      if (buildNumber >= 2016 && buildNumber <= 2999) {
+        return new BuildNumber(productCode, Format.YEAR_BASED, buildNumber, 0);
+      }
+      
       baselineVersion = getBaseLineForHistoricBuilds(buildNumber);
+      return new BuildNumber(productCode, Format.HISTORIC, baselineVersion, 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;
+    if (SNAPSHOT.equals(code) || BUILD_NUMBER.equals(code)) {
+      return SNAPSHOT_VALUE;
     }
+    if (STAR.equals(code)) {
+      return SNAPSHOT_VALUE;
+    }
+    
     try {
       return Integer.parseInt(code);
     }
@@ -181,8 +218,18 @@ public class BuildNumber implements Comparable<BuildNumber> {
 
   @Override
   public int compareTo(@NotNull BuildNumber o) {
-    if (myBaselineVersion == o.myBaselineVersion) return myBuildNumber - o.myBuildNumber;
-    return myBaselineVersion - o.myBaselineVersion;
+    int[] c1 = myComponents;
+    int[] c2 = o.myComponents;
+    
+    for (int i = 0; i < Math.min(c1.length, c2.length); i++) {
+      if (c1[i] == c2[i] && c1[i] == SNAPSHOT_VALUE) return 0;
+      if (c1[i] == SNAPSHOT_VALUE) return 1;
+      if (c2[i] == SNAPSHOT_VALUE) return -1;
+
+      int result = c1[i] - c2[i];
+      if (result != 0) return result;
+    }
+    return c1.length - c2.length;
   }
 
   @NotNull
@@ -191,11 +238,21 @@ public class BuildNumber implements Comparable<BuildNumber> {
   }
 
   public int getBaselineVersion() {
-    return myBaselineVersion;
+    return myFormat == Format.YEAR_BASED ? (myComponents[0] * 10 + myComponents[1]) : myComponents[0];
   }
 
+  @Deprecated
   public int getBuildNumber() {
-    return myBuildNumber;
+    return myFormat == Format.YEAR_BASED ? -1 : myComponents[1];
+  }
+
+  public int[] getComponents() {
+    return myComponents;
+  }
+
+  @NotNull
+  public Format getFormat() {
+    return myFormat;
   }
 
   @Override
@@ -205,10 +262,9 @@ public class BuildNumber implements Comparable<BuildNumber> {
 
     BuildNumber that = (BuildNumber)o;
 
-    if (myBaselineVersion != that.myBaselineVersion) return false;
-    if (myBuildNumber != that.myBuildNumber) return false;
+    if (myFormat != that.myFormat) return false;
     if (!myProductCode.equals(that.myProductCode)) return false;
-    if (!Comparing.equal(myAttemptInfo, that.myAttemptInfo)) return false;
+    if (!Arrays.equals(myComponents, that.myComponents)) return false;
 
     return true;
   }
@@ -216,18 +272,13 @@ public class BuildNumber implements Comparable<BuildNumber> {
   @Override
   public int hashCode() {
     int result = myProductCode.hashCode();
-    result = 31 * result + myBaselineVersion;
-    result = 31 * result + myBuildNumber;
-    if (myAttemptInfo != null) result = 31 * result + myAttemptInfo.hashCode();
+    result = 31 * result + Arrays.hashCode(myComponents);
+    result = 31 * result + myFormat.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 Holder.TOP_BASELINE_VERSION; // SNAPSHOTS
-    }
-
     if (bn >= 10000) {
       return 88; // Maia, 9x builds
     }
@@ -276,10 +327,9 @@ public class BuildNumber implements Comparable<BuildNumber> {
   }
 
   public boolean isSnapshot() {
-    return myBuildNumber == Integer.MAX_VALUE;
-  }
-
-  public String asStringWithAllDetails() {
-    return asString(true, true);
+    for (int each : myComponents) {
+      if (each == SNAPSHOT_VALUE) return true;
+    }
+    return false;
   }
 }
\ No newline at end of file
index c2283c4fa02a0de5a2b5c947b9101cc673aa468b..c0c73c33c950ee348ab4cad85db8b287fb1c3f98 100644 (file)
@@ -19,9 +19,6 @@ import org.junit.Test;
 
 import static org.junit.Assert.*;
 
-/**
- * @author max
- */
 public class BuildNumberTest {
   @Test
   public void empty() {
@@ -31,15 +28,110 @@ public class BuildNumberTest {
 
   @Test
   public void historicBuild() {
-    assertEquals(new BuildNumber("", 75, 7512), BuildNumber.fromString("7512"));
+    assertEquals(new BuildNumber("", BuildNumber.Format.HISTORIC, 75, 7512), BuildNumber.fromString("7512"));
+    assertEquals("75.7512", BuildNumber.fromString("7512").asString());
+  }
+
+  @Test
+  public void branchBasedBuild() throws Exception {
+    assertParsed(BuildNumber.fromString("145"), 145, 0, BuildNumber.Format.BRANCH_BASED, "145.0");
+    assertParsed(BuildNumber.fromString("145.1"), 145, 1, BuildNumber.Format.BRANCH_BASED, "145.1");
+    assertParsed(BuildNumber.fromString("145.1.2"), 145, 1, BuildNumber.Format.BRANCH_BASED, "145.1.2");
+    assertParsed(BuildNumber.fromString("IU-145.1.2"), 145, 1, BuildNumber.Format.BRANCH_BASED, "IU-145.1.2");
+    assertParsed(BuildNumber.fromString("IU-145.SNAPSHOT"), 145, BuildNumber.SNAPSHOT_VALUE, BuildNumber.Format.BRANCH_BASED, "IU-145.SNAPSHOT");
+    assertParsed(BuildNumber.fromString("IU-145.1.SNAPSHOT"), 145, 1, BuildNumber.Format.BRANCH_BASED, "IU-145.1.SNAPSHOT");
+  }
+
+  @Test
+  public void yearBasedBuild() throws Exception {
+    assertParsed(BuildNumber.fromString("2016"), 20160, -1, BuildNumber.Format.YEAR_BASED, "2016.0");
+    assertParsed(BuildNumber.fromString("2016.1"), 20161, -1, BuildNumber.Format.YEAR_BASED, "2016.1");
+    assertParsed(BuildNumber.fromString("2016.1.2"), 20161, -1, BuildNumber.Format.YEAR_BASED, "2016.1.2");
+    assertParsed(BuildNumber.fromString("IU-2016.1.2"), 20161, -1, BuildNumber.Format.YEAR_BASED, "IU-2016.1.2");
+    assertParsed(BuildNumber.fromString("2016.1.2.3"), 20161, -1, BuildNumber.Format.YEAR_BASED, "2016.1.2.3");
+    assertParsed(BuildNumber.fromString("IU-2016.1.2.3"), 20161, -1, BuildNumber.Format.YEAR_BASED, "IU-2016.1.2.3");
+    assertParsed(BuildNumber.fromString("2016.1.2.3.4"), 20161, -1, BuildNumber.Format.YEAR_BASED, "2016.1.2.3.4");
+    assertParsed(BuildNumber.fromString("IU-2016.1.2.3.4"), 20161, -1, BuildNumber.Format.YEAR_BASED, "IU-2016.1.2.3.4");
+    
+    assertParsed(BuildNumber.fromString("IU-2016.1.SNAPSHOT"), 20161, -1, BuildNumber.Format.YEAR_BASED, "IU-2016.1.SNAPSHOT");
+    assertParsed(BuildNumber.fromString("IU-2016.1.SNAPSHOT.1"), 20161, -1, BuildNumber.Format.YEAR_BASED, "IU-2016.1.SNAPSHOT.1");
+  }
+  
+  private static void assertParsed(BuildNumber n, int expectedBaseline, int expectedBuildNumber, BuildNumber.Format based, String asString) {
+    assertEquals(expectedBaseline, n.getBaselineVersion());
+    assertEquals(expectedBuildNumber, n.getBuildNumber());
+    assertEquals(asString, n.asString());
+    assertEquals(based, n.getFormat());
+  }
+
+  @Test
+  public void comparingVersion() throws Exception {
+    assertTrue(BuildNumber.fromString("2016").compareTo(BuildNumber.fromString("2016.1")) < 0);
+    assertTrue(BuildNumber.fromString("2015.1").compareTo(BuildNumber.fromString("2016")) < 0);
+    assertTrue(BuildNumber.fromString("2016.0").compareTo(BuildNumber.fromString("2016.0")) == 0);
+    
+    assertTrue(BuildNumber.fromString("2016.1").compareTo(BuildNumber.fromString("2016.1")) == 0);
+    assertTrue(BuildNumber.fromString("2016.1.1").compareTo(BuildNumber.fromString("2016.1.1")) == 0);
+    assertTrue(BuildNumber.fromString("2016.1.1.1").compareTo(BuildNumber.fromString("2016.1.1.1")) == 0);
+    
+    assertTrue(BuildNumber.fromString("2016.1").compareTo(BuildNumber.fromString("2016.1.1")) < 0);
+    assertTrue(BuildNumber.fromString("2016.1").compareTo(BuildNumber.fromString("2016.1.1.1")) < 0);
+    assertTrue(BuildNumber.fromString("2016.1.1").compareTo(BuildNumber.fromString("2016.1.1.1")) < 0);
+
+    assertTrue(BuildNumber.fromString("2016.1").compareTo(BuildNumber.fromString("2016.2")) < 0);
+    assertTrue(BuildNumber.fromString("2016.1").compareTo(BuildNumber.fromString("2016.2.1")) < 0);
+    assertTrue(BuildNumber.fromString("2016.1.1").compareTo(BuildNumber.fromString("2016.2")) < 0);
+    assertTrue(BuildNumber.fromString("2016.1.1").compareTo(BuildNumber.fromString("2016.2.1")) < 0);
+
+    assertTrue(BuildNumber.fromString("146.1").compareTo(BuildNumber.fromString("2016.1")) < 0);
+    assertTrue(BuildNumber.fromString("146.9.9").compareTo(BuildNumber.fromString("2016.1")) < 0);
+    
+    assertTrue(BuildNumber.fromString("2016.2").compareTo(BuildNumber.fromString("2016.2.*")) < 0);
+    assertTrue(BuildNumber.fromString("2016.2").compareTo(BuildNumber.fromString("2016.3.*")) < 0);
+    assertTrue(BuildNumber.fromString("2016.2").compareTo(BuildNumber.fromString("2016.1.*")) > 0);
+    assertTrue(BuildNumber.fromString("2016.2.2.2.2").compareTo(BuildNumber.fromString("2016.2.*")) < 0);
+    assertTrue(BuildNumber.fromString("2016.2.*").compareTo(BuildNumber.fromString("2016.2.2.2.2")) > 0);
+
+    assertTrue(BuildNumber.fromString("145.1").compareTo(BuildNumber.fromString("145.*")) < 0);
+    assertTrue(BuildNumber.fromString("145.1.1").compareTo(BuildNumber.fromString("145.*")) < 0);
+    assertTrue(BuildNumber.fromString("145.1").compareTo(BuildNumber.fromString("146.*")) < 0);
+    assertTrue(BuildNumber.fromString("145.1").compareTo(BuildNumber.fromString("144.*")) > 0);
+
+    assertTrue(BuildNumber.fromString("145.SNAPSHOT").compareTo(BuildNumber.fromString("145.*")) == 0);
+    assertTrue(BuildNumber.fromString("145.*").compareTo(BuildNumber.fromString("145.SNAPSHOT")) == 0);
+
+    assertTrue(BuildNumber.fromString("2016.1.SNAPSHOT").compareTo(BuildNumber.fromString("2016.1.*")) == 0);
+    assertTrue(BuildNumber.fromString("2016.1.*").compareTo(BuildNumber.fromString("2016.1.SNAPSHOT")) == 0);
+    
+    assertTrue(BuildNumber.fromString("2016.1.SNAPSHOT").compareTo(BuildNumber.fromString("2016.*")) < 0);
+    assertTrue(BuildNumber.fromString("2016.*").compareTo(BuildNumber.fromString("2016.1.SNAPSHOT")) > 0);
+
+    assertTrue(BuildNumber.fromString("2016.SNAPSHOT").compareTo(BuildNumber.fromString("2016.1.*")) > 0);
+    assertTrue(BuildNumber.fromString("2016.1.*").compareTo(BuildNumber.fromString("2016.SNAPSHOT")) < 0);
   }
 
   @Test
   public void isSnapshot() {
+    assertTrue(BuildNumber.fromString("SNAPSHOT").isSnapshot());
     assertTrue(BuildNumber.fromString("__BUILD_NUMBER__").isSnapshot());
     assertTrue(BuildNumber.fromString("IU-90.SNAPSHOT").isSnapshot());
+    
     assertTrue(BuildNumber.fromString("IC-90.*").isSnapshot());
     assertFalse(BuildNumber.fromString("90.9999999").isSnapshot());
+    
+    assertFalse(BuildNumber.fromString("2016.1").isSnapshot());
+    assertTrue(BuildNumber.fromString("2016.1.SNAPSHOT").isSnapshot());
+    assertTrue(BuildNumber.fromString("2016.1.SNAPSHOT.1").isSnapshot());
+  }
+
+  @Test
+  public void devSnapshotVersion() throws Exception {
+    BuildNumber b = BuildNumber.fromString("__BUILD_NUMBER__");
+    assertTrue(b.asString(), b.getBaselineVersion() >= 2016);
+    assertEquals(b.asString(), -1, b.getBuildNumber());
+    assertTrue(b.isSnapshot());
+    
+    assertEquals(BuildNumber.fromString("__BUILD_NUMBER__"), BuildNumber.fromString("SNAPSHOT"));
   }
 
   @Test
@@ -49,5 +141,37 @@ public class BuildNumberTest {
     assertTrue(BuildNumber.fromString("IU-90.SNAPSHOT").compareTo(BuildNumber.fromString("RM-100.12345")) < 0);
     assertTrue(BuildNumber.fromString("IU-90.SNAPSHOT").compareTo(BuildNumber.fromString("RM-100.SNAPSHOT")) < 0);
     assertTrue(BuildNumber.fromString("IU-90.SNAPSHOT").compareTo(BuildNumber.fromString("RM-90.SNAPSHOT")) == 0);
+    
+    assertTrue(BuildNumber.fromString("2016.1.SNAPSHOT").compareTo(BuildNumber.fromString("2016.1.1")) > 0);
+    assertTrue(BuildNumber.fromString("2016.1.1").compareTo(BuildNumber.fromString("2016.1.SNAPSHOT")) < 0);
+    
+    assertTrue(BuildNumber.fromString("2016.1.SNAPSHOT").compareTo(BuildNumber.fromString("2016.1.SNAPSHOT")) == 0);
+    
+    assertTrue(BuildNumber.fromString("2016.1.1.SNAPSHOT").compareTo(BuildNumber.fromString("2016.1.1.1")) > 0);
+    assertTrue(BuildNumber.fromString("2016.1.1.1").compareTo(BuildNumber.fromString("2016.1.1.SNAPSHOT")) < 0);
+    
+    assertTrue(BuildNumber.fromString("2016.1.1.SNAPSHOT").compareTo(BuildNumber.fromString("2016.1.1.SNAPSHOT")) == 0);
+    
+    assertTrue(BuildNumber.fromString("2016.1.SNAPSHOT.1").compareTo(BuildNumber.fromString("2016.1.1.1")) > 0);
+    assertTrue(BuildNumber.fromString("2016.1.1.1").compareTo(BuildNumber.fromString("2016.1.SNAPSHOT.1")) < 0);
+    
+    assertTrue(BuildNumber.fromString("2016.1.SNAPSHOT.1").compareTo(BuildNumber.fromString("2016.1.1.SNAPSHOT")) > 0);
+    assertTrue(BuildNumber.fromString("2016.1.1.SNAPSHOT").compareTo(BuildNumber.fromString("2016.1.SNAPSHOT.1")) < 0);
+    
+    assertTrue(BuildNumber.fromString("2016.1.SNAPSHOT.1").compareTo(BuildNumber.fromString("2016.1.SNAPSHOT.SNAPSHOT")) == 0);
+    assertTrue(BuildNumber.fromString("2016.1.SNAPSHOT.SNAPSHOT").compareTo(BuildNumber.fromString("2016.1.SNAPSHOT.1")) == 0);
+  }
+
+  @Test
+  public void fallbackVersion() throws Exception {
+    assertParsed(BuildNumber.fallback(), 29991, -1, BuildNumber.Format.YEAR_BASED, "2999.1.SNAPSHOT");
+    assertEquals(BuildNumber.Format.YEAR_BASED, BuildNumber.fallback().getFormat());
+    assertTrue(BuildNumber.fallback().isSnapshot());
+    
+    assertTrue(BuildNumber.fallback().compareTo(BuildNumber.fromString("7512")) > 0);
+    assertTrue(BuildNumber.fallback().compareTo(BuildNumber.fromString("145")) > 0);
+    assertTrue(BuildNumber.fallback().compareTo(BuildNumber.fromString("145.12")) > 0);
+    assertTrue(BuildNumber.fallback().compareTo(BuildNumber.fromString("2016.1")) > 0);
+    assertTrue(BuildNumber.fallback().compareTo(BuildNumber.fromString("2016.1.2")) > 0);
   }
 }
\ No newline at end of file
index 7cb9ca82a35e6fd23070be4e69c9cf45e520df24..95ae2cde1bb05cbe76d97904a04e530bbf308b8d 100644 (file)
@@ -19,8 +19,8 @@ import com.intellij.notification.Notification;
 import com.intellij.notification.NotificationType;
 import com.intellij.notification.Notifications;
 import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ApplicationNamesInfo;
 import com.intellij.openapi.application.ModalityState;
-import com.intellij.openapi.application.ex.ApplicationInfoEx;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.fileChooser.FileChooser;
 import com.intellij.openapi.fileChooser.FileChooserDescriptor;
@@ -267,14 +267,14 @@ public class EclipseImportBuilder extends ProjectImportBuilder<String> implement
         }
       }
       if (!files.isEmpty()) {
-        final int resultCode = Messages.showYesNoCancelDialog(ApplicationInfoEx.getInstanceEx().getFullApplicationName() +
-                                                              " module files found:\n" +
-                                                              StringUtil.join(files,new Function<File, String>() {
+        final int resultCode = Messages.showYesNoCancelDialog(ApplicationNamesInfo.getInstance().getFullProductName() +
+                                                                                   " module files found:\n" +
+                                                                                   StringUtil.join(files,new Function<File, String>() {
                                                                 public String fun(File file) {
                                                                   return file.getPath();
                                                                 }
                                                               }, "\n") +
-                                                              ".\n Would you like to reuse them?", "Module Files Found",
+                                                                                   ".\n Would you like to reuse them?", "Module Files Found",
                                                               Messages.getQuestionIcon());
         if (resultCode != Messages.YES) {
           if (resultCode == Messages.NO) {
index 558a5667c5986b22e4848e27583e1c4713a81c1d..027eb8364725139212aa6b1e22047cd8a80a0946 100644 (file)
@@ -57,11 +57,7 @@ class IdeaCommitMessageFormatter : CommitMessageFormatter {
         append(appInfo.fullVersion)
       }
       else {
-        var buildString = build.asStringWithoutProductCode()
-        if (build.buildNumber == Integer.MAX_VALUE) {
-          buildString = buildString.replace(".SNAPSHOT", "")
-        }
-        append(buildString)
+        append(build.asStringWithoutProductCodeAndSnapshot())
       }
     }
   }
index 86abebec64f56e1fc9d25df80093e32d6c8dccc3..4fc2287a3a730696becbff45ca54fd8d569bb293 100644 (file)
@@ -1,6 +1,6 @@
 <component>
+  <version major="2016" minor="2" eap="true"/>
   <company name="JetBrains s.r.o." url="https://www.jetbrains.com/?fromIDE"/>
-  <version major="2" minor="0.4" eap="false"/>
   <build number="__BUILD_NUMBER__" date="__BUILD_DATE__"/>
   <logo url="/pycharm_edu_logo.png" textcolor="313131" progressColor="20d688" progressY="281" progressTailIcon="/community_progress_tail.png"/>
   <about url="/pycharm_edu_about.png" logoX="300" logoY="265" logoW="75" logoH="30" foreground="313131"
index e1c20f1d2f4ff30801b6d52a63456bc3f37f960a..6f44d77b14f52fc2899cbbda4154655ae5df0e2c 100644 (file)
@@ -1,6 +1,6 @@
 <component>
+  <version major="2016" minor="2" eap="true"/>
   <company name="JetBrains s.r.o." url="https://www.jetbrains.com/?fromIDE"/>
-  <version major="2016" minor="1" eap="true"/>
   <build number="__BUILD_NUMBER__" date="__BUILD_DATE__"/>
   <logo url="/pycharm_core_logo.png" textcolor="dddddd" progressColor="08f5d6" progressX="0" progressY="280" progressHeight="3"/>
   <about url="/pycharm_core_about.png" logoX="300" logoY="265" logoW="75" logoH="30" foreground="313131"