/*
 * Copyright 2012-present Facebook, Inc.
 *
 * 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.facebook.buck.core.rules.resolver.impl;

import com.facebook.buck.core.description.BaseDescription;
import com.facebook.buck.core.description.MetadataProvidingDescription;
import com.facebook.buck.core.model.BuildTarget;
import com.facebook.buck.core.model.targetgraph.TargetGraph;
import com.facebook.buck.core.model.targetgraph.TargetNode;
import com.facebook.buck.core.rules.ActionGraphBuilder;
import com.facebook.buck.util.types.Pair;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.util.Optional;
import java.util.concurrent.ExecutionException;

/** Implementation of the metadata system for ActionGraphBuilders. */
final class ActionGraphBuilderMetadataCache {
  private final ActionGraphBuilder graphBuilder;
  private final TargetGraph targetGraph;
  private final LoadingCache<Pair<BuildTarget, Class<?>>, Optional<?>> metadataCache;

  ActionGraphBuilderMetadataCache(
      ActionGraphBuilder graphBuilder, TargetGraph targetGraph, int initialCapacity) {
    // Precondition (not checked): graphBuilder has the same targetGraph as the one passed in
    this.graphBuilder = graphBuilder;
    this.targetGraph = targetGraph;
    this.metadataCache =
        CacheBuilder.newBuilder().initialCapacity(initialCapacity).build(new MetadataCacheLoader());
  }

  @SuppressWarnings("unchecked")
  <T> Optional<T> requireMetadata(BuildTarget target, Class<T> metadataClass) {
    try {
      return (Optional<T>) metadataCache.get(new Pair<>(target, metadataClass));
    } catch (ExecutionException e) {
      throw new RuntimeException(e);
    }
  }

  private final class MetadataCacheLoader
      extends CacheLoader<Pair<BuildTarget, Class<?>>, Optional<?>> {
    @Override
    public Optional<?> load(Pair<BuildTarget, Class<?>> key) {
      TargetNode<?> node = targetGraph.get(key.getFirst());
      return load(node, key.getSecond());
    }

    @SuppressWarnings("unchecked")
    private <T, U> Optional<U> load(TargetNode<T> node, Class<U> metadataClass) {
      T arg = node.getConstructorArg();
      if (metadataClass.isAssignableFrom(arg.getClass())) {
        return Optional.of(metadataClass.cast(arg));
      }

      BaseDescription<?> description = node.getDescription();
      if (!(description instanceof MetadataProvidingDescription)) {
        return Optional.empty();
      }
      MetadataProvidingDescription<T> metadataProvidingDescription =
          (MetadataProvidingDescription<T>) description;
      return metadataProvidingDescription.createMetadata(
          node.getBuildTarget(),
          graphBuilder,
          node.getCellNames(),
          arg,
          node.getSelectedVersions(),
          metadataClass);
    }
  }
}

@@ -1001,32 +1001,106 @@
 onGraphBuilder;%0A
+import com.facebook.buck.util.exceptions.BuckUncheckedExecutionException;%0A
 import com.faceb
@@ -2583,24 +2583,38 @@
 etFirst());%0A
+      try %7B%0A  
       return
@@ -2639,24 +2639,212 @@
 tSecond());%0A
+      %7D catch (Exception e) %7B%0A        throw new BuckUncheckedExecutionException(%0A            e, %22Error loading metadata for target %25s, class %25s%22, key.getFirst(), key.getSecond());%0A      %7D%0A
     %7D%0A%0A    @

Add target name to error thrown from creating metadata