Add file extension property to Skylark

RELNOTES[NEW]: Files now have an "extension" property in Skylark.

--
MOS_MIGRATED_REVID=136425934
diff --git a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
index b7ac0df..80d060d6 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
@@ -300,6 +300,11 @@
     return getExecPath().getBaseName();
   }
 
+  @SkylarkCallable(name = "extension", structField = true, doc = "The file extension of this file.")
+  public final String getExtension() {
+    return getExecPath().getFileExtension();
+  }
+
   /**
    * Returns the artifact owner. May be null.
    */
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java b/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java
index 2980e29..c6a6ad6 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java
@@ -512,6 +512,20 @@
   }
 
   /**
+   * Returns the file extension of this path, excluding the period, or "" if there is no extension.
+   */
+  public String getFileExtension() {
+    String baseName = getBaseName();
+
+    int lastIndex = baseName.lastIndexOf('.');
+    if (lastIndex != -1) {
+      return baseName.substring(lastIndex + 1);
+    }
+
+    return "";
+  }
+
+  /**
    * Returns a relative path fragment to this path, relative to
    * {@code ancestorDirectory}.
    * <p>
diff --git a/src/test/java/com/google/devtools/build/lib/actions/ArtifactTest.java b/src/test/java/com/google/devtools/build/lib/actions/ArtifactTest.java
index e43ddd1..81b64b2 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/ArtifactTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/ArtifactTest.java
@@ -33,16 +33,14 @@
 import com.google.devtools.build.lib.testutil.Scratch;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
-
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
 @RunWith(JUnit4.class)
 public class ArtifactTest {
   private Scratch scratch;
@@ -149,6 +147,13 @@
   }
 
   @Test
+  public void testGetExtension() throws Exception {
+    Root root = Root.asSourceRoot(scratch.dir("/foo"));
+    Artifact javaFile = new Artifact(scratch.file("/foo/Bar.java"), root);
+    assertThat(javaFile.getExtension()).isEqualTo("java");
+  }
+
+  @Test
   public void testMangledPath() {
     String path = "dir/sub_dir/name:end";
     assertEquals("dir_Ssub_Udir_Sname_Cend", Actions.escapedPath(path));
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/PathFragmentTest.java b/src/test/java/com/google/devtools/build/lib/vfs/PathFragmentTest.java
index 71b7dec..c4ac7c1 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/PathFragmentTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/PathFragmentTest.java
@@ -280,6 +280,20 @@
     assertThat(new PathFragment("").getBaseName()).isEmpty();
   }
 
+  @Test
+  public void testFileExtension() throws Exception {
+    assertThat(new PathFragment("foo.bar").getFileExtension()).isEqualTo("bar");
+    assertThat(new PathFragment("foo.barr").getFileExtension()).isEqualTo("barr");
+    assertThat(new PathFragment("foo.b").getFileExtension()).isEqualTo("b");
+    assertThat(new PathFragment("foo.").getFileExtension()).isEmpty();
+    assertThat(new PathFragment("foo").getFileExtension()).isEmpty();
+    assertThat(new PathFragment(".").getFileExtension()).isEmpty();
+    assertThat(new PathFragment("").getFileExtension()).isEmpty();
+    assertThat(new PathFragment("foo/bar.baz").getFileExtension()).isEqualTo("baz");
+    assertThat(new PathFragment("foo.bar.baz").getFileExtension()).isEqualTo("baz");
+    assertThat(new PathFragment("foo.bar/baz").getFileExtension()).isEmpty();
+  }
+
   private static void assertPath(String expected, PathFragment actual) {
     assertEquals(expected, actual.getPathString());
   }