Clean up absolutize and make it public for use in a subsequent change.

--
MOS_MIGRATED_REVID=116058328
diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/TargetPattern.java b/src/main/java/com/google/devtools/build/lib/cmdline/TargetPattern.java
index 8b49a2a..f8c1a83 100644
--- a/src/main/java/com/google/devtools/build/lib/cmdline/TargetPattern.java
+++ b/src/main/java/com/google/devtools/build/lib/cmdline/TargetPattern.java
@@ -31,6 +31,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
+import java.util.regex.Pattern;
 
 import javax.annotation.concurrent.Immutable;
 
@@ -334,17 +335,17 @@
 
     private final PackageIdentifier packageIdentifier;
     private final String suffix;
-    private final boolean isAbsolute;
+    private final boolean wasOriginallyAbsolute;
     private final boolean rulesOnly;
     private final boolean checkWildcardConflict;
 
     private TargetsInPackage(String originalPattern, String offset,
-        PackageIdentifier packageIdentifier, String suffix, boolean isAbsolute, boolean rulesOnly,
-        boolean checkWildcardConflict) {
+        PackageIdentifier packageIdentifier, String suffix, boolean wasOriginallyAbsolute,
+        boolean rulesOnly, boolean checkWildcardConflict) {
       super(Type.TARGETS_IN_PACKAGE, originalPattern, offset);
       this.packageIdentifier = packageIdentifier;
       this.suffix = Preconditions.checkNotNull(suffix);
-      this.isAbsolute = isAbsolute;
+      this.wasOriginallyAbsolute = wasOriginallyAbsolute;
       this.rulesOnly = rulesOnly;
       this.checkWildcardConflict = checkWildcardConflict;
     }
@@ -396,7 +397,7 @@
         return false;
       }
       TargetsInPackage that = (TargetsInPackage) o;
-      return isAbsolute == that.isAbsolute && rulesOnly == that.rulesOnly
+      return wasOriginallyAbsolute == that.wasOriginallyAbsolute && rulesOnly == that.rulesOnly
           && checkWildcardConflict == that.checkWildcardConflict
           && getOriginalPattern().equals(that.getOriginalPattern())
           && packageIdentifier.equals(that.packageIdentifier) && suffix.equals(that.suffix);
@@ -404,8 +405,8 @@
 
     @Override
     public int hashCode() {
-      return Objects.hash(getType(), getOriginalPattern(), packageIdentifier, suffix, isAbsolute,
-          rulesOnly, checkWildcardConflict);
+      return Objects.hash(getType(), getOriginalPattern(), packageIdentifier, suffix,
+          wasOriginallyAbsolute, rulesOnly, checkWildcardConflict);
     }
 
     /**
@@ -417,7 +418,7 @@
      */
     private <T> ResolvedTargets<T> getWildcardConflict(TargetPatternResolver<T> resolver)
         throws InterruptedException {
-      if (!isAbsolute) {
+      if (!wasOriginallyAbsolute) {
         return null;
       }
 
@@ -515,6 +516,10 @@
 
   @Immutable
   public static final class Parser {
+    // A valid pattern either starts with exactly 0 slashes (relative pattern) or exactly two
+    // slashes (absolute pattern).
+    private static final Pattern VALID_SLASH_PREFIX = Pattern.compile("(//)?([^/]|$)");
+
     // TODO(bazel-team): Merge the Label functionality that requires similar constants into this
     // class.
     /**
@@ -600,14 +605,16 @@
 
         pattern = pattern.substring(pkgStart);
       }
-      final boolean isAbsolute = pattern.startsWith("//");
 
-      // We now absolutize non-absolute target patterns.
-      pattern = isAbsolute ? pattern.substring(2) : absolutize(pattern);
-      // Check for common errors.
-      if (pattern.startsWith("/")) {
-        throw new TargetParsingException("not a relative path or label: '" + pattern + "'");
+      if (!VALID_SLASH_PREFIX.matcher(pattern).lookingAt()) {
+        throw new TargetParsingException("not a valid absolute pattern (absolute target patterns "
+            + "must start with exactly two slashes): '" + pattern + "'");
       }
+
+      final boolean wasOriginallyAbsolute = pattern.startsWith("//");
+      // We now ensure the relativeDirectory is applied to relative patterns.
+      pattern = absolutize(pattern).substring(2);
+
       if (pattern.isEmpty()) {
         throw new TargetParsingException("the empty string is not a valid target");
       }
@@ -659,7 +666,7 @@
               "Invalid package name '" + packagePart + "': " + e.getMessage());
         }
         return new TargetsInPackage(originalPattern, relativeDirectory, packageIdentifier,
-            targetPart, isAbsolute, true, true);
+            targetPart, wasOriginallyAbsolute, true, true);
       }
 
       if (ALL_TARGETS_IN_SUFFIXES.contains(targetPart)) {
@@ -671,10 +678,10 @@
               "Invalid package name '" + packagePart + "': " + e.getMessage());
         }
         return new TargetsInPackage(originalPattern, relativeDirectory, packageIdentifier,
-            targetPart, isAbsolute, false, true);
+            targetPart, wasOriginallyAbsolute, false, true);
       }
 
-      if (includesRepo || isAbsolute || pattern.contains(":")) {
+      if (includesRepo || wasOriginallyAbsolute || pattern.contains(":")) {
         PackageIdentifier packageIdentifier;
         String fullLabel = repository.getName() + "//" + pattern;
         try {
@@ -710,19 +717,20 @@
 
     /**
      * Absolutizes the target pattern to the offset.
-     * Patterns starting with "/" are absolute and not modified.
+     * Patterns starting with "//" are absolute and not modified.
+     * Assumes the given pattern is not invalid wrt leading "/"s.
      *
      * If the offset is "foo":
-     *   absolutize(":bar") --> "foo:bar"
-     *   absolutize("bar") --> "foo/bar"
-     *   absolutize("/biz/bar") --> "biz/bar" (absolute)
-     *   absolutize("biz:bar") --> "foo/biz:bar"
+     *   absolutize(":bar") --> "//foo:bar"
+     *   absolutize("bar") --> "//foo/bar"
+     *   absolutize("//biz/bar") --> "//biz/bar" (absolute)
+     *   absolutize("biz:bar") --> "//foo/biz:bar"
      *
      * @param pattern The target pattern to parse.
      * @return the pattern, absolutized to the offset if approprate.
      */
-    private String absolutize(String pattern) {
-      if (relativeDirectory.isEmpty() || pattern.startsWith("/")) {
+    public String absolutize(String pattern) {
+      if (pattern.startsWith("//")) {
         return pattern;
       }
 
@@ -730,9 +738,9 @@
       // but it doesn't work when the pattern starts with ":".
       // "foo".getRelative(":all") would return "foo/:all", where we
       // really want "foo:all".
-      return pattern.startsWith(":")
-          ? relativeDirectory + pattern
-          : relativeDirectory + "/" + pattern;
+      return pattern.startsWith(":") || relativeDirectory.isEmpty()
+          ? "//" + relativeDirectory + pattern
+          : "//" + relativeDirectory + "/" + pattern;
     }
   }