Supporting wildcards in max/min version specifications in VariationsService.
Adds a method CompareToWildcardString that will return -1/0/1, similar to 
CompareTo, when a version is smaller, equal to or greater than a wildcard
string such as "1.2.*". Added a method IsValidWildcardString that validates
the format of a wildcard string, and slightly refactored the Version class
to avoid code duplication. For example, CompareToWildcardString and CompareTo
share the new method CompareVersionComponents.

BUG=127077
TEST=See tests for VariationsService, Version

Review URL: https://chromiumcodereview.appspot.com/10576003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@145468 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/base/version.cc b/base/version.cc
index 1f9bd20..01bf84a 100644
--- a/base/version.cc
+++ b/base/version.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -11,6 +11,70 @@
 #include "base/string_split.h"
 #include "base/string_util.h"
 
+namespace {
+
+// Parses the |numbers| vector representing the different numbers
+// inside the version string and constructs a vector of valid integers. It stops
+// when it reaches an invalid item (including the wildcard character). |parsed|
+// is the resulting integer vector. Function returns true if all numbers were
+// parsed successfully, false otherwise.
+bool ParseVersionNumbers(const std::string& version_str,
+                         std::vector<uint16>* parsed) {
+  std::vector<std::string> numbers;
+  base::SplitString(version_str, '.', &numbers);
+  if (numbers.empty())
+    return false;
+
+  for (std::vector<std::string>::const_iterator it = numbers.begin();
+       it != numbers.end(); ++it) {
+    int num;
+    if (!base::StringToInt(*it, &num))
+      return false;
+
+    if (num < 0)
+      return false;
+
+    const uint16 max = 0xFFFF;
+    if (num > max)
+      return false;
+
+    // This throws out things like +3, or 032.
+    if (base::IntToString(num) != *it)
+      return false;
+
+    parsed->push_back(static_cast<uint16>(num));
+  }
+  return true;
+}
+
+// Compares version components in |components1| with components in
+// |components2|. Returns -1, 0 or 1 if |components1| is greater than, equal to,
+// or less than |components2|, respectively.
+int CompareVersionComponents(const std::vector<uint16>& components1,
+                             const std::vector<uint16>& components2) {
+  const size_t count = std::min(components1.size(), components2.size());
+  for (size_t i = 0; i < count; ++i) {
+    if (components1[i] > components2[i])
+      return 1;
+    if (components1[i] < components2[i])
+      return -1;
+  }
+  if (components1.size() > components2.size()) {
+    for (size_t i = count; i < components1.size(); ++i) {
+      if (components1[i] > 0)
+        return 1;
+    }
+  } else if (components1.size() < components2.size()) {
+    for (size_t i = count; i < components2.size(); ++i) {
+      if (components2[i] > 0)
+        return -1;
+    }
+  }
+  return 0;
+}
+
+}  // namespace
+
 Version::Version() {
 }
 
@@ -18,26 +82,10 @@
 }
 
 Version::Version(const std::string& version_str) {
-  std::vector<std::string> numbers;
-  base::SplitString(version_str, '.', &numbers);
-  if (numbers.empty())
-    return;
   std::vector<uint16> parsed;
-  for (std::vector<std::string>::iterator i = numbers.begin();
-       i != numbers.end(); ++i) {
-    int num;
-    if (!base::StringToInt(*i, &num))
-      return;
-    if (num < 0)
-      return;
-    const uint16 max = 0xFFFF;
-    if (num > max)
-      return;
-    // This throws out things like +3, or 032.
-    if (base::IntToString(num) != *i)
-      return;
-    parsed.push_back(static_cast<uint16>(num));
-  }
+  if (!ParseVersionNumbers(version_str, &parsed))
+    return;
+
   components_.swap(parsed);
 }
 
@@ -45,6 +93,16 @@
   return (!components_.empty());
 }
 
+// static
+bool Version::IsValidWildcardString(const std::string& wildcard_string) {
+  std::string version_string = wildcard_string;
+  if (EndsWith(wildcard_string.c_str(), ".*", false))
+    version_string = wildcard_string.substr(0, wildcard_string.size() - 2);
+
+  Version version(version_string);
+  return version.IsValid();
+}
+
 bool Version::IsOlderThan(const std::string& version_str) const {
   Version proposed_ver(version_str);
   if (!proposed_ver.IsValid())
@@ -52,6 +110,43 @@
   return (CompareTo(proposed_ver) < 0);
 }
 
+int Version::CompareToWildcardString(const std::string& wildcard_string) const {
+  DCHECK(IsValid());
+  DCHECK(Version::IsValidWildcardString(wildcard_string));
+
+  // Default behavior if the string doesn't end with a wildcard.
+  if (!EndsWith(wildcard_string.c_str(), ".*", false)) {
+    Version version(wildcard_string);
+    DCHECK(version.IsValid());
+    return CompareTo(version);
+  }
+
+  std::vector<uint16> parsed;
+  const bool success = ParseVersionNumbers(
+      wildcard_string.substr(0, wildcard_string.length() - 2), &parsed);
+  DCHECK(success);
+  const int comparison = CompareVersionComponents(components_, parsed);
+  // If the version is smaller than the wildcard version's |parsed| vector,
+  // then the wildcard has no effect (e.g. comparing 1.2.3 and 1.3.*) and the
+  // version is still smaller. Same logic for equality (e.g. comparing 1.2.2 to
+  // 1.2.2.* is 0 regardless of the wildcard). Under this logic,
+  // 1.2.0.0.0.0 compared to 1.2.* is 0.
+  if (comparison == -1 || comparison == 0)
+    return comparison;
+
+  // Catch the case where the digits of |parsed| are found in |components_|,
+  // which means that the two are equal since |parsed| has a trailing "*".
+  // (e.g. 1.2.3 vs. 1.2.* will return 0). All other cases return 1 since
+  // components is greater (e.g. 3.2.3 vs 1.*).
+  DCHECK_GT(parsed.size(), 0UL);
+  const size_t min_num_comp = std::min(components_.size(), parsed.size());
+  for (size_t i = 0; i < min_num_comp; ++i) {
+    if (components_[i] != parsed[i])
+      return 1;
+  }
+  return 0;
+}
+
 // TODO(cpu): remove this method.
 Version* Version::GetVersionFromString(const std::string& version_str) {
   Version* vers = new Version(version_str);
@@ -77,23 +172,7 @@
 int Version::CompareTo(const Version& other) const {
   DCHECK(IsValid());
   DCHECK(other.IsValid());
-  size_t count = std::min(components_.size(), other.components_.size());
-  for (size_t i = 0; i < count; ++i) {
-    if (components_[i] > other.components_[i])
-      return 1;
-    if (components_[i] < other.components_[i])
-      return -1;
-  }
-  if (components_.size() > other.components_.size()) {
-    for (size_t i = count; i < components_.size(); ++i)
-      if (components_[i] > 0)
-        return 1;
-  } else if (components_.size() < other.components_.size()) {
-    for (size_t i = count; i < other.components_.size(); ++i)
-      if (other.components_[i] > 0)
-        return -1;
-  }
-  return 0;
+  return CompareVersionComponents(components_, other.components_);
 }
 
 const std::string Version::GetString() const {