/* * Copyright 2015-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.ide.intellij; import com.facebook.buck.android.AndroidPrebuiltAarDescription; import com.facebook.buck.android.AndroidPrebuiltAarDescriptionArg; import com.facebook.buck.ide.intellij.model.IjLibrary; import com.facebook.buck.ide.intellij.model.IjLibraryFactory; import com.facebook.buck.ide.intellij.model.IjLibraryFactoryResolver; import com.facebook.buck.jvm.java.PrebuiltJarDescription; import com.facebook.buck.jvm.java.PrebuiltJarDescriptionArg; import com.facebook.buck.rules.Description; import com.facebook.buck.rules.SourcePath; import com.facebook.buck.rules.TargetNode; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import java.nio.file.Path; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; /** * Filters out all of the targets which can be represented as IntelliJ prebuilts from the set of * TargetNodes and allows resolving those as dependencies of modules. */ class DefaultIjLibraryFactory extends IjLibraryFactory { /** Rule describing how to create a {@link IjLibrary} from a {@link TargetNode}. */ private interface IjLibraryRule { void applyRule(TargetNode<?, ?> targetNode, IjLibrary.Builder library); } /** * Rule describing how to create a {@link IjLibrary} from a {@link TargetNode}. * * @param <T> the type of the TargetNode. */ abstract class TypedIjLibraryRule<T> implements IjLibraryRule { abstract Class<? extends Description<?>> getDescriptionClass(); abstract void apply(TargetNode<T, ?> targetNode, IjLibrary.Builder library); @Override @SuppressWarnings({"unchecked", "rawtypes"}) public void applyRule(TargetNode<?, ?> targetNode, IjLibrary.Builder library) { apply((TargetNode) targetNode, library); } } private Map<Class<? extends Description<?>>, IjLibraryRule> libraryRuleIndex = new HashMap<>(); private Set<String> uniqueLibraryNamesSet = new HashSet<>(); private IjLibraryFactoryResolver libraryFactoryResolver; private Map<TargetNode<?, ?>, Optional<IjLibrary>> libraryCache; public DefaultIjLibraryFactory(IjLibraryFactoryResolver libraryFactoryResolver) { this.libraryFactoryResolver = libraryFactoryResolver; addToIndex(new AndroidPrebuiltAarLibraryRule()); addToIndex(new PrebuiltJarLibraryRule()); libraryCache = new HashMap<>(); } private void addToIndex(TypedIjLibraryRule<?> rule) { Preconditions.checkArgument(!libraryRuleIndex.containsKey(rule.getDescriptionClass())); libraryRuleIndex.put(rule.getDescriptionClass(), rule); } @Override public Optional<IjLibrary> getLibrary(TargetNode<?, ?> target) { Optional<IjLibrary> library = libraryCache.get(target); if (library == null) { library = createLibrary(target); libraryCache.put(target, library); } return library; } private Optional<IjLibraryRule> getRule(TargetNode<?, ?> targetNode) { IjLibraryRule rule = libraryRuleIndex.get(targetNode.getDescription().getClass()); if (rule == null) { rule = libraryFactoryResolver .getPathIfJavaLibrary(targetNode) .map(libraryFactoryResolver::getPath) .map(JavaLibraryRule::new) .orElse(null); } return Optional.ofNullable(rule); } private Optional<IjLibrary> createLibrary(final TargetNode<?, ?> targetNode) { return getRule(targetNode) .map( rule -> { // Use a "library_" prefix so that the names don't clash with module names. String libraryName = Util.intelliJLibraryName(targetNode.getBuildTarget()); Preconditions.checkState( !uniqueLibraryNamesSet.contains(libraryName), "Trying to use the same library name for different targets."); IjLibrary.Builder libraryBuilder = IjLibrary.builder(); rule.applyRule(targetNode, libraryBuilder); libraryBuilder.setName(libraryName); libraryBuilder.setTargets(ImmutableSet.of(targetNode.getBuildTarget())); return libraryBuilder.build(); }); } private static class JavaLibraryRule implements IjLibraryRule { private Path binaryJarPath; public JavaLibraryRule(Path binaryJarPath) { this.binaryJarPath = binaryJarPath; } @Override public void applyRule(TargetNode<?, ?> targetNode, IjLibrary.Builder library) { library.addBinaryJars(binaryJarPath); } } private class AndroidPrebuiltAarLibraryRule extends TypedIjLibraryRule<AndroidPrebuiltAarDescriptionArg> { @Override public Class<? extends Description<?>> getDescriptionClass() { return AndroidPrebuiltAarDescription.class; } @Override public void apply( TargetNode<AndroidPrebuiltAarDescriptionArg, ?> targetNode, IjLibrary.Builder library) { Optional<SourcePath> libraryPath = libraryFactoryResolver.getPathIfJavaLibrary(targetNode); libraryPath.ifPresent(path -> library.addBinaryJars(libraryFactoryResolver.getPath(path))); AndroidPrebuiltAarDescriptionArg arg = targetNode.getConstructorArg(); arg.getSourceJar() .ifPresent( sourcePath -> library.addSourceJars(libraryFactoryResolver.getPath(sourcePath))); arg.getJavadocUrl().ifPresent(library::addJavadocUrls); } } private class PrebuiltJarLibraryRule extends TypedIjLibraryRule<PrebuiltJarDescriptionArg> { @Override public Class<? extends Description<?>> getDescriptionClass() { return PrebuiltJarDescription.class; } @Override public void apply( TargetNode<PrebuiltJarDescriptionArg, ?> targetNode, IjLibrary.Builder library) { PrebuiltJarDescriptionArg arg = targetNode.getConstructorArg(); library.addBinaryJars(libraryFactoryResolver.getPath(arg.getBinaryJar())); arg.getSourceJar() .ifPresent(sourceJar -> library.addSourceJars(libraryFactoryResolver.getPath(sourceJar))); arg.getJavadocUrl().ifPresent(library::addJavadocUrls); } } }