|  | // 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.devtools.build.lib.cmdline.LabelValidator; | 
|  | import com.google.devtools.build.lib.syntax.Label; | 
|  | import com.google.devtools.build.lib.vfs.PathFragment; | 
|  |  | 
|  | /** | 
|  | * A class that represents some packages that are included in the visibility list of a rule. | 
|  | */ | 
|  | public abstract class PackageSpecification { | 
|  | private static final String PACKAGE_LABEL = "__pkg__"; | 
|  | private static final String SUBTREE_LABEL = "__subpackages__"; | 
|  | private static final String ALL_BENEATH_SUFFIX = "/..."; | 
|  | public static final PackageSpecification EVERYTHING = | 
|  | new AllPackagesBeneath(new PathFragment("")); | 
|  |  | 
|  | public abstract boolean containsPackage(PathFragment packageName); | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return toString().hashCode(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object that) { | 
|  | if (this == that) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (!(that instanceof PackageSpecification)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return this.toString().equals(that.toString()); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Parses a string as a visibility specification. | 
|  | * Throws {@link InvalidPackageSpecificationException} if the label cannot be parsed. | 
|  | * | 
|  | * <p>Note that these strings are different from what {@link #fromLabel} understands. | 
|  | */ | 
|  | public static PackageSpecification fromString(final String spec) | 
|  | throws InvalidPackageSpecificationException { | 
|  | String result = spec; | 
|  | boolean allBeneath = false; | 
|  |  | 
|  | if (result.startsWith("//")) { | 
|  | result = spec.substring(2); | 
|  | } else { | 
|  | throw new InvalidPackageSpecificationException("invalid package label: " + spec); | 
|  | } | 
|  |  | 
|  | if (result.indexOf(':') >= 0) { | 
|  | throw new InvalidPackageSpecificationException("invalid package label: " + spec); | 
|  | } | 
|  |  | 
|  | if (result.equals("...")) { | 
|  | // Special case: //... will not end in /... | 
|  | return EVERYTHING; | 
|  | } | 
|  |  | 
|  | if (result.endsWith(ALL_BENEATH_SUFFIX)) { | 
|  | allBeneath = true; | 
|  | result = result.substring(0, result.length() - ALL_BENEATH_SUFFIX.length()); | 
|  | } | 
|  |  | 
|  | String errorMessage = LabelValidator.validatePackageName(result); | 
|  | if (errorMessage == null) { | 
|  | return allBeneath ? | 
|  | new AllPackagesBeneath(new PathFragment(result)) : | 
|  | new SinglePackage(new PathFragment(result)); | 
|  | } else { | 
|  | throw new InvalidPackageSpecificationException(errorMessage); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Parses a label as a visibility specification. returns null if the label cannot be parsed. | 
|  | * | 
|  | * <p>Note that these strings are different from what {@link #fromString} understands. | 
|  | */ | 
|  | public static PackageSpecification fromLabel(Label label) { | 
|  | if (label.getName().equals(PACKAGE_LABEL)) { | 
|  | return new SinglePackage(label.getPackageFragment()); | 
|  | } else if (label.getName().equals(SUBTREE_LABEL)) { | 
|  | return new AllPackagesBeneath(label.getPackageFragment()); | 
|  | } else { | 
|  | return null; | 
|  | } | 
|  | } | 
|  |  | 
|  | private static class SinglePackage extends PackageSpecification { | 
|  | private PathFragment singlePackageName; | 
|  |  | 
|  | public SinglePackage(PathFragment packageName) { | 
|  | this.singlePackageName = packageName; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean containsPackage(PathFragment packageName) { | 
|  | return this.singlePackageName.equals(packageName); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return singlePackageName.toString(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private static class AllPackagesBeneath extends PackageSpecification { | 
|  | private PathFragment prefix; | 
|  |  | 
|  | public AllPackagesBeneath(PathFragment prefix) { | 
|  | this.prefix = prefix; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean containsPackage(PathFragment packageName) { | 
|  | return packageName.startsWith(prefix); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return prefix.equals(new PathFragment("")) ? "..." : prefix.toString() + "/..."; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Exception class to be thrown when a specification cannot be parsed. | 
|  | */ | 
|  | public static class InvalidPackageSpecificationException extends Exception { | 
|  | public InvalidPackageSpecificationException(String message) { | 
|  | super(message); | 
|  | } | 
|  | } | 
|  | } |