Update from Google.

--
MOE_MIGRATED_REVID=85702957
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageIdentifier.java b/src/main/java/com/google/devtools/build/lib/packages/PackageIdentifier.java
new file mode 100644
index 0000000..7b16e3c
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageIdentifier.java
@@ -0,0 +1,271 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// 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.google.devtools.build.lib.packages;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ComparisonChain;
+import com.google.devtools.build.lib.syntax.Label.SyntaxException;
+import com.google.devtools.build.lib.util.StringCanonicalizer;
+import com.google.devtools.build.lib.util.StringUtilities;
+import com.google.devtools.build.lib.vfs.Canonicalizer;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.Objects;
+
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * Uniquely identifies a package, given a repository name and a package's path fragment.
+ *
+ * <p>The repository the build is happening in is the <i>default workspace</i>, and is identified
+ * by the workspace name "". Other repositories can be named in the WORKSPACE file.  These
+ * workspaces are prefixed by {@literal @}.</p>
+ */
+@Immutable
+public final class PackageIdentifier implements Comparable<PackageIdentifier>, Serializable {
+
+  /**
+   * A human-readable name for the repository.
+   */
+  public static final class RepositoryName {
+    private final String name;
+
+    /**
+     * Makes sure that name is a valid repository name and creates a new RepositoryName using it.
+     * @throws SyntaxException if the name is invalid.
+     */
+    public static RepositoryName create(String name) throws SyntaxException {
+      String errorMessage = validate(name);
+      if (errorMessage != null) {
+        errorMessage = "invalid repository name '"
+            + StringUtilities.sanitizeControlChars(name) + "': " + errorMessage;
+        throw new SyntaxException(errorMessage);
+      }
+      return new RepositoryName(StringCanonicalizer.intern(name));
+    }
+
+    private RepositoryName(String name) {
+      this.name = name;
+    }
+
+    /**
+     * Performs validity checking.  Returns null on success, an error message otherwise.
+     */
+    private static String validate(String name) {
+      if (name.isEmpty()) {
+        return null;
+      }
+
+      if (!name.startsWith("@")) {
+        return "workspace name must start with '@'";
+      }
+
+      // "@" isn't a valid workspace name.
+      if (name.length() == 1) {
+        return "empty workspace name";
+      }
+
+      // Check for any character outside of [/0-9A-Z_a-z-]. Try to evaluate the
+      // conditional quickly (by looking in decreasing order of character class
+      // likelihood).
+      for (int i = name.length() - 1; i >= 1; --i) {
+        char c = name.charAt(i);
+        if ((c < 'a' || c > 'z') && c != '_' && c != '-'
+            && (c < '0' || c > '9') && (c < 'A' || c > 'Z')) {
+          return "workspace names may contain only A-Z, a-z, 0-9, '-' and '_'";
+        }
+      }
+      return null;
+    }
+
+    /**
+     * Returns the repository name without the leading "{@literal @}".  For the default repository,
+     * returns "".
+     */
+    public String strippedName() {
+      if (name.isEmpty()) {
+        return name;
+      }
+      return name.substring(1);
+    }
+
+    /**
+     * Returns if this is the default repository, that is, {@link #name} is "".
+     */
+    public boolean isDefault() {
+      return name.isEmpty();
+    }
+
+    /**
+     * Returns the repository name, with leading "{@literal @}" (or "" for the default repository).
+     */
+    @Override
+    public String toString() {
+      return name;
+    }
+
+    @Override
+    public boolean equals(Object object) {
+      if (this == object) {
+        return true;
+      }
+      if (object instanceof RepositoryName) {
+        return name.equals(((RepositoryName) object).name);
+      }
+      return false;
+    }
+
+    @Override
+    public int hashCode() {
+      return name.hashCode();
+    }
+  }
+
+  public static final String DEFAULT_REPOSITORY = "";
+
+  /**
+   * Helper for serializing PackageIdentifiers.
+   *
+   * <p>PackageIdentifier's field should be final, but then it couldn't be deserialized. This
+   * allows the fields to be deserialized and copied into a new PackageIdentifier.</p>
+   */
+  private static final class SerializationProxy implements Serializable {
+    PackageIdentifier packageId;
+
+    public SerializationProxy(PackageIdentifier packageId) {
+      this.packageId = packageId;
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+      out.writeObject(packageId.repository.toString());
+      out.writeObject(packageId.pkgName);
+    }
+
+    private void readObject(ObjectInputStream in)
+        throws IOException, ClassNotFoundException {
+      try {
+        packageId = new PackageIdentifier((String) in.readObject(), (PathFragment) in.readObject());
+      } catch (SyntaxException e) {
+        throw new IOException("Error serializing package identifier: " + e.getMessage());
+      }
+    }
+
+    @SuppressWarnings("unused")
+    private void readObjectNoData() throws ObjectStreamException {
+    }
+
+    private Object readResolve() {
+      return packageId;
+    }
+  }
+
+  // Temporary factory for identifiers without explicit repositories.
+  // TODO(bazel-team): remove all usages of this.
+  public static PackageIdentifier createInDefaultRepo(String name) {
+    return createInDefaultRepo(new PathFragment(name));
+  }
+
+  public static PackageIdentifier createInDefaultRepo(PathFragment name) {
+    try {
+      return new PackageIdentifier(DEFAULT_REPOSITORY, name);
+    } catch (SyntaxException e) {
+      throw new IllegalArgumentException("could not create package identifier for " + name
+          + ": " + e.getMessage());
+    }
+  }
+
+  /**
+   * The identifier for this repository. This is either "" or prefixed with an "@",
+   * e.g., "@myrepo".
+   */
+  private final RepositoryName repository;
+
+  /** The name of the package. Canonical (i.e. x.equals(y) <=> x==y). */
+  private final PathFragment pkgName;
+
+  public PackageIdentifier(String repository, PathFragment pkgName) throws SyntaxException {
+    this(RepositoryName.create(repository), pkgName);
+  }
+
+  public PackageIdentifier(RepositoryName repository, PathFragment pkgName) {
+    Preconditions.checkNotNull(repository);
+    Preconditions.checkNotNull(pkgName);
+    this.repository = repository;
+    this.pkgName = Canonicalizer.fragments().intern(pkgName);
+  }
+
+  private Object writeReplace() throws ObjectStreamException {
+    return new SerializationProxy(this);
+  }
+
+  private void readObject(ObjectInputStream in)
+      throws IOException, ClassNotFoundException {
+    throw new IOException("Serialization is allowed only by proxy");
+  }
+
+  @SuppressWarnings("unused")
+  private void readObjectNoData() throws ObjectStreamException {
+  }
+
+  public RepositoryName getRepository() {
+    return repository;
+  }
+
+  public PathFragment getPackageFragment() {
+    return pkgName;
+  }
+
+  /**
+   * Returns the name of this package.
+   *
+   * <p>There are certain places that expect the path fragment as the package name ('foo/bar') as a
+   * package identifier. This isn't specific enough for packages in other repositories, so their
+   * stringified version is '@baz//foo/bar'.</p>
+   */
+  @Override
+  public String toString() {
+    return (repository.isDefault() ? "" : repository + "//") + pkgName;
+  }
+
+  @Override
+  public boolean equals(Object object) {
+    if (this == object) {
+      return true;
+    }
+    if (object instanceof PackageIdentifier) {
+      PackageIdentifier that = (PackageIdentifier) object;
+      return repository.equals(that.repository) && pkgName.equals(that.pkgName);
+    }
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(repository, pkgName);
+  }
+
+  @Override
+  public int compareTo(PackageIdentifier that) {
+    return ComparisonChain.start()
+        .compare(repository.toString(), that.repository.toString())
+        .compare(pkgName, that.pkgName)
+        .result();
+  }
+}
\ No newline at end of file