bep: Properly URI encode file paths

RELNOTES: The Build Event Protocol's File.uri field is now properly
encoded according to RFC2396.
PiperOrigin-RevId: 168674865
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/Path.java b/src/main/java/com/google/devtools/build/lib/vfs/Path.java
index a1436b5..5c3c7bf 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/Path.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/Path.java
@@ -31,6 +31,8 @@
 import java.lang.ref.Reference;
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.WeakReference;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.Collection;
 import java.util.IdentityHashMap;
 import java.util.Objects;
@@ -382,6 +384,39 @@
   }
 
   /**
+   * Returns the path encoded as an {@link URI}.
+   *
+   * <p>This concrete implementation returns URIs with "file" as the scheme.
+   * For Example:
+   *  - On Unix the path "/tmp/foo bar.txt" will be encoded as
+   *    "file:///tmp/foo%20bar.txt".
+   *  - On Windows the path "C:\Temp\Foo Bar.txt" will be encoded as
+   *    "file:///C:/Temp/Foo%20Bar.txt"
+   *
+   * <p>Implementors extending this class for special filesystems will likely need to override
+   * this method.
+   *
+   * @throws URISyntaxException if the URI cannot be constructed.
+   */
+  public URI toURI() {
+    String ps = getPathString();
+    if (!ps.startsWith("/")) {
+      // On Windows URI's need to start with a '/'. i.e. C:\Foo\Bar would be file:///C:/Foo/Bar
+      ps = "/" + ps;
+    }
+    try {
+      return new URI("file",
+          // Needs to be "" instead of null, so that toString() will append "//" after the scheme.
+          // We need this for backwards compatibility reasons as some consumers of the BEP are
+          // broken.
+          "",
+          ps, null, null);
+    } catch (URISyntaxException e) {
+      throw new IllegalStateException(e);
+    }
+  }
+
+  /**
    * Returns the path as a string.
    */
   public String getPathString() {