|  | // Copyright 2016 The Bazel Authors. 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.analysis; | 
|  |  | 
|  | import com.google.common.collect.ImmutableList; | 
|  | import com.google.common.collect.ImmutableMap; | 
|  | import com.google.devtools.build.lib.cmdline.Label; | 
|  | import com.google.devtools.build.lib.events.Location; | 
|  | import com.google.devtools.build.lib.packages.Attribute; | 
|  | import com.google.devtools.build.lib.packages.AttributeMap; | 
|  | import com.google.devtools.build.lib.packages.RuleClass; | 
|  | import com.google.devtools.build.lib.syntax.Type; | 
|  | import java.util.Collection; | 
|  |  | 
|  | /** | 
|  | * An {@link AttributeMap} that supports attribute type queries on both a rule and its aspects and | 
|  | * attribute value queries on the rule. | 
|  | * | 
|  | * <p>An attribute type query is anything accessible from {@link Attribute} (i.e. anything about how | 
|  | * the attribute is integrated into the {@link RuleClass}). An attribute value query is anything | 
|  | * related to the actual value an attribute takes. | 
|  | * | 
|  | * <p>For example, given {@code deps = [":adep"]}, checking that {@code deps} exists or that it's | 
|  | * type is {@link com.google.devtools.build.lib.packages.BuildType#LABEL_LIST} are type queries. | 
|  | * Checking that its value is explicitly set in the BUILD File or that its value {@code [":adep"]} | 
|  | * are value queries.. | 
|  | * | 
|  | * <p>Value queries on aspect attributes trigger {@link UnsupportedOperationException}. | 
|  | */ | 
|  | class AspectAwareAttributeMapper implements AttributeMap { | 
|  | private final AttributeMap ruleAttributes; | 
|  | private final ImmutableMap<String, Attribute> aspectAttributes; | 
|  |  | 
|  | public AspectAwareAttributeMapper(AttributeMap ruleAttributes, | 
|  | ImmutableMap<String, Attribute> aspectAttributes) { | 
|  | this.ruleAttributes = ruleAttributes; | 
|  | this.aspectAttributes = aspectAttributes; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Don't use this except where absolutely necessary. This exposes internal implementation details. | 
|  | */ | 
|  | ImmutableMap<String, Attribute> getAspectAttributes() { | 
|  | return aspectAttributes; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String getName() { | 
|  | return ruleAttributes.getName(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String getRuleClassName() { | 
|  | return ruleAttributes.getRuleClassName(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Label getLabel() { | 
|  | return ruleAttributes.getLabel(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public <T> T get(String attributeName, Type<T> type) { | 
|  | if (ruleAttributes.has(attributeName, type)) { | 
|  | return ruleAttributes.get(attributeName, type); | 
|  | } else { | 
|  | Attribute attribute = aspectAttributes.get(attributeName); | 
|  | if (attribute == null) { | 
|  | throw new IllegalArgumentException(String.format( | 
|  | "no attribute '%s' in either %s or its aspects", | 
|  | attributeName, ruleAttributes.getLabel())); | 
|  | } else if (attribute.getType() != type) { | 
|  | throw new IllegalArgumentException(String.format( | 
|  | "attribute %s has type %s, not expected type %s", | 
|  | attributeName, attribute.getType(), type)); | 
|  | } else { | 
|  | throw new UnsupportedOperationException( | 
|  | String.format( | 
|  | "Attribute '%s' comes from an aspect. " | 
|  | + "Value retrieval for aspect attributes is not supported.", | 
|  | attributeName)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isConfigurable(String attributeName) { | 
|  | return ruleAttributes.isConfigurable(attributeName); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Iterable<String> getAttributeNames() { | 
|  | return ImmutableList.<String>builder() | 
|  | .addAll(ruleAttributes.getAttributeNames()) | 
|  | .addAll(aspectAttributes.keySet()) | 
|  | .build(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Type<?> getAttributeType(String attrName) { | 
|  | Type<?> type = ruleAttributes.getAttributeType(attrName); | 
|  | if (type != null) { | 
|  | return type; | 
|  | } else { | 
|  | Attribute attribute = aspectAttributes.get(attrName); | 
|  | return attribute != null ? attribute.getType() : null; | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Attribute getAttributeDefinition(String attrName) { | 
|  | Attribute attribute = ruleAttributes.getAttributeDefinition(attrName); | 
|  | if (attribute != null) { | 
|  | return attribute; | 
|  | } else { | 
|  | return aspectAttributes.get(attrName); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isAttributeValueExplicitlySpecified(String attributeName) { | 
|  | return ruleAttributes.isAttributeValueExplicitlySpecified(attributeName); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Collection<DepEdge> visitLabels() throws InterruptedException { | 
|  | throw new UnsupportedOperationException("rule + aspects label visition is not supported"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String getPackageDefaultHdrsCheck() { | 
|  | return ruleAttributes.getPackageDefaultHdrsCheck(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Boolean getPackageDefaultTestOnly() { | 
|  | return ruleAttributes.getPackageDefaultTestOnly(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String getPackageDefaultDeprecation() { | 
|  | return ruleAttributes.getPackageDefaultDeprecation(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ImmutableList<String> getPackageDefaultCopts() { | 
|  | return ruleAttributes.getPackageDefaultCopts(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean has(String attrName) { | 
|  | if (ruleAttributes.has(attrName)) { | 
|  | return true; | 
|  | } else { | 
|  | return aspectAttributes.containsKey(attrName); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public <T> boolean has(String attrName, Type<T> type) { | 
|  | if (ruleAttributes.has(attrName, type)) { | 
|  | return true; | 
|  | } else { | 
|  | return aspectAttributes.containsKey(attrName) | 
|  | && aspectAttributes.get(attrName).getType() == type; | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Location getAttributeLocation(String attrName) { | 
|  | if (ruleAttributes.has(attrName)) { | 
|  | return ruleAttributes.getAttributeLocation(attrName); | 
|  | } else { | 
|  | return Location.BUILTIN; | 
|  | } | 
|  | } | 
|  | } |